mirror of
https://github.com/crowdsecurity/crowdsec.git
synced 2025-05-10 20:05:55 +02:00
557 lines
21 KiB
Go
557 lines
21 KiB
Go
package apiserver
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/go-openapi/strfmt"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
|
"github.com/crowdsecurity/crowdsec/pkg/csplugin"
|
|
"github.com/crowdsecurity/crowdsec/pkg/database"
|
|
"github.com/crowdsecurity/crowdsec/pkg/models"
|
|
)
|
|
|
|
const (
|
|
passwordAuthType = "password"
|
|
apiKeyAuthType = "apikey"
|
|
)
|
|
|
|
type LAPI struct {
|
|
router *gin.Engine
|
|
loginResp models.WatcherAuthResponse
|
|
bouncerKey string
|
|
DBConfig *csconfig.DatabaseCfg
|
|
DBClient *database.Client
|
|
}
|
|
|
|
func SetupLAPITest(t *testing.T, ctx context.Context) LAPI {
|
|
t.Helper()
|
|
router, loginResp, config := InitMachineTest(t, ctx)
|
|
|
|
APIKey, dbClient := CreateTestBouncer(t, ctx, config.API.Server.DbConfig)
|
|
|
|
return LAPI{
|
|
router: router,
|
|
loginResp: loginResp,
|
|
bouncerKey: APIKey,
|
|
DBConfig: config.API.Server.DbConfig,
|
|
DBClient: dbClient,
|
|
}
|
|
}
|
|
|
|
func (l *LAPI) InsertAlertFromFile(t *testing.T, ctx context.Context, path string) *httptest.ResponseRecorder {
|
|
alertReader := GetAlertReaderFromFile(t, path)
|
|
return l.RecordResponse(t, ctx, http.MethodPost, "/v1/alerts", alertReader, "password")
|
|
}
|
|
|
|
func (l *LAPI) RecordResponse(t *testing.T, ctx context.Context, verb string, url string, body *strings.Reader, authType string) *httptest.ResponseRecorder {
|
|
w := httptest.NewRecorder()
|
|
req, err := http.NewRequestWithContext(ctx, verb, url, body)
|
|
require.NoError(t, err)
|
|
|
|
switch authType {
|
|
case apiKeyAuthType:
|
|
req.Header.Add("X-Api-Key", l.bouncerKey)
|
|
case passwordAuthType:
|
|
AddAuthHeaders(req, l.loginResp)
|
|
default:
|
|
t.Fatal("auth type not supported")
|
|
}
|
|
|
|
// Port is required for gin to properly parse the client IP
|
|
req.RemoteAddr = "127.0.0.1:1234"
|
|
|
|
l.router.ServeHTTP(w, req)
|
|
|
|
return w
|
|
}
|
|
|
|
func InitMachineTest(t *testing.T, ctx context.Context) (*gin.Engine, models.WatcherAuthResponse, csconfig.Config) {
|
|
router, config := NewAPITest(t, ctx)
|
|
loginResp := LoginToTestAPI(t, ctx, router, config)
|
|
|
|
return router, loginResp, config
|
|
}
|
|
|
|
func LoginToTestAPI(t *testing.T, ctx context.Context, router *gin.Engine, config csconfig.Config) models.WatcherAuthResponse {
|
|
body := CreateTestMachine(t, ctx, router, "")
|
|
ValidateMachine(t, ctx, "test", config.API.Server.DbConfig)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequestWithContext(ctx, http.MethodPost, "/v1/watchers/login", strings.NewReader(body))
|
|
req.Header.Add("User-Agent", UserAgent)
|
|
router.ServeHTTP(w, req)
|
|
|
|
loginResp := models.WatcherAuthResponse{}
|
|
err := json.NewDecoder(w.Body).Decode(&loginResp)
|
|
require.NoError(t, err)
|
|
|
|
return loginResp
|
|
}
|
|
|
|
func AddAuthHeaders(request *http.Request, authResponse models.WatcherAuthResponse) {
|
|
request.Header.Add("User-Agent", UserAgent)
|
|
request.Header.Add("Authorization", fmt.Sprintf("Bearer %s", authResponse.Token))
|
|
}
|
|
|
|
func TestSimulatedAlert(t *testing.T) {
|
|
ctx := t.Context()
|
|
lapi := SetupLAPITest(t, ctx)
|
|
lapi.InsertAlertFromFile(t, ctx, "./tests/alert_minibulk+simul.json")
|
|
alertContent := GetAlertReaderFromFile(t, "./tests/alert_minibulk+simul.json")
|
|
// exclude decision in simulation mode
|
|
|
|
w := lapi.RecordResponse(t, ctx, "GET", "/v1/alerts?simulated=false", alertContent, "password")
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
assert.Contains(t, w.Body.String(), `"message":"Ip 91.121.79.178 performed crowdsecurity/ssh-bf (6 events over `)
|
|
assert.NotContains(t, w.Body.String(), `"message":"Ip 91.121.79.179 performed crowdsecurity/ssh-bf (6 events over `)
|
|
// include decision in simulation mode
|
|
|
|
w = lapi.RecordResponse(t, ctx, "GET", "/v1/alerts?simulated=true", alertContent, "password")
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
assert.Contains(t, w.Body.String(), `"message":"Ip 91.121.79.178 performed crowdsecurity/ssh-bf (6 events over `)
|
|
assert.Contains(t, w.Body.String(), `"message":"Ip 91.121.79.179 performed crowdsecurity/ssh-bf (6 events over `)
|
|
}
|
|
|
|
func TestCreateAlert(t *testing.T) {
|
|
ctx := t.Context()
|
|
lapi := SetupLAPITest(t, ctx)
|
|
// Create Alert with invalid format
|
|
|
|
w := lapi.RecordResponse(t, ctx, http.MethodPost, "/v1/alerts", strings.NewReader("test"), "password")
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
assert.JSONEq(t, `{"message":"invalid character 'e' in literal true (expecting 'r')"}`, w.Body.String())
|
|
|
|
// Create Alert with invalid input
|
|
alertContent := GetAlertReaderFromFile(t, "./tests/invalidAlert_sample.json")
|
|
|
|
w = lapi.RecordResponse(t, ctx, http.MethodPost, "/v1/alerts", alertContent, "password")
|
|
assert.Equal(t, http.StatusInternalServerError, w.Code)
|
|
assert.JSONEq(t,
|
|
`{"message":"validation failure list:\n0.scenario in body is required\n0.scenario_hash in body is required\n0.scenario_version in body is required\n0.simulated in body is required\n0.source in body is required"}`,
|
|
w.Body.String())
|
|
|
|
// Create Valid Alert
|
|
w = lapi.InsertAlertFromFile(t, ctx, "./tests/alert_sample.json")
|
|
assert.Equal(t, http.StatusCreated, w.Code)
|
|
assert.Equal(t, `["1"]`, w.Body.String())
|
|
}
|
|
|
|
func TestCreateAllowlistedAlert(t *testing.T) {
|
|
ctx := context.Background()
|
|
lapi := SetupLAPITest(t, ctx)
|
|
|
|
allowlist, err := lapi.DBClient.CreateAllowList(ctx, "test", "test", "", false)
|
|
require.NoError(t, err)
|
|
added, err := lapi.DBClient.AddToAllowlist(ctx, allowlist, []*models.AllowlistItem{
|
|
{
|
|
Value: "10.0.0.0/24",
|
|
},
|
|
{
|
|
Value: "192.168.0.0/24",
|
|
Expiration: strfmt.DateTime(time.Now().Add(-time.Hour)), // Expired item
|
|
},
|
|
{
|
|
Value: "127.0.0.1",
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 3, added)
|
|
|
|
// Create Alert with allowlisted IP
|
|
alertContent := GetAlertReaderFromFile(t, "./tests/alert_allowlisted.json")
|
|
w := lapi.RecordResponse(t, ctx, http.MethodPost, "/v1/alerts", alertContent, "password")
|
|
assert.Equal(t, http.StatusCreated, w.Code)
|
|
|
|
// We should have no alert as the IP is allowlisted
|
|
w = lapi.RecordResponse(t, ctx, "GET", "/v1/alerts", emptyBody, "password")
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
assert.Equal(t, "null", w.Body.String())
|
|
|
|
// Create Alert with expired allowlisted IP
|
|
alertContent = GetAlertReaderFromFile(t, "./tests/alert_allowlisted_expired.json")
|
|
w = lapi.RecordResponse(t, ctx, http.MethodPost, "/v1/alerts", alertContent, "password")
|
|
assert.Equal(t, http.StatusCreated, w.Code)
|
|
|
|
// We should have an alert as the IP is allowlisted but the item is expired
|
|
w = lapi.RecordResponse(t, ctx, "GET", "/v1/alerts", emptyBody, "password")
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
assert.Contains(t, w.Body.String(), "192.168.0.42")
|
|
|
|
// Create Alert with allowlisted IP but with decisions (manual ban)
|
|
alertContent = GetAlertReaderFromFile(t, "./tests/alert_sample.json")
|
|
w = lapi.RecordResponse(t, ctx, http.MethodPost, "/v1/alerts", alertContent, "password")
|
|
assert.Equal(t, http.StatusCreated, w.Code)
|
|
|
|
// We should have an alert as the IP is allowlisted but the alert has decisions
|
|
w = lapi.RecordResponse(t, ctx, "GET", "/v1/alerts", emptyBody, "password")
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
assert.Contains(t, w.Body.String(), "127.0.0.1")
|
|
}
|
|
|
|
func TestCreateAlertChannels(t *testing.T) {
|
|
ctx := t.Context()
|
|
apiServer, config := NewAPIServer(t, ctx)
|
|
apiServer.controller.PluginChannel = make(chan csplugin.ProfileAlert)
|
|
err := apiServer.InitController()
|
|
require.NoError(t, err)
|
|
|
|
loginResp := LoginToTestAPI(t, ctx, apiServer.router, config)
|
|
lapi := LAPI{router: apiServer.router, loginResp: loginResp}
|
|
|
|
var (
|
|
pd csplugin.ProfileAlert
|
|
wg sync.WaitGroup
|
|
)
|
|
|
|
wg.Add(1)
|
|
|
|
go func() {
|
|
pd = <-apiServer.controller.PluginChannel
|
|
|
|
wg.Done()
|
|
}()
|
|
|
|
lapi.InsertAlertFromFile(t, ctx, "./tests/alert_ssh-bf.json")
|
|
wg.Wait()
|
|
assert.Len(t, pd.Alert.Decisions, 1)
|
|
apiServer.Close()
|
|
}
|
|
|
|
func TestAlertListFilters(t *testing.T) {
|
|
ctx := t.Context()
|
|
lapi := SetupLAPITest(t, ctx)
|
|
lapi.InsertAlertFromFile(t, ctx, "./tests/alert_ssh-bf.json")
|
|
alertContent := GetAlertReaderFromFile(t, "./tests/alert_ssh-bf.json")
|
|
|
|
// bad filter
|
|
|
|
w := lapi.RecordResponse(t, ctx, "GET", "/v1/alerts?test=test", alertContent, "password")
|
|
assert.Equal(t, http.StatusInternalServerError, w.Code)
|
|
assert.JSONEq(t, `{"message":"Filter parameter 'test' is unknown (=test): invalid filter"}`, w.Body.String())
|
|
|
|
// get without filters
|
|
|
|
w = lapi.RecordResponse(t, ctx, "GET", "/v1/alerts", emptyBody, "password")
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
// check alert and decision
|
|
assert.Contains(t, w.Body.String(), "Ip 91.121.79.195 performed 'crowdsecurity/ssh-bf' (6 events over ")
|
|
assert.Contains(t, w.Body.String(), `scope":"Ip","simulated":false,"type":"ban","value":"91.121.79.195"`)
|
|
|
|
// test decision_type filter (ok)
|
|
|
|
w = lapi.RecordResponse(t, ctx, "GET", "/v1/alerts?decision_type=ban", emptyBody, "password")
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
assert.Contains(t, w.Body.String(), "Ip 91.121.79.195 performed 'crowdsecurity/ssh-bf' (6 events over ")
|
|
assert.Contains(t, w.Body.String(), `scope":"Ip","simulated":false,"type":"ban","value":"91.121.79.195"`)
|
|
|
|
// test decision_type filter (bad value)
|
|
|
|
w = lapi.RecordResponse(t, ctx, "GET", "/v1/alerts?decision_type=ratata", emptyBody, "password")
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
assert.Equal(t, "null", w.Body.String())
|
|
|
|
// test scope (ok)
|
|
|
|
w = lapi.RecordResponse(t, ctx, "GET", "/v1/alerts?scope=Ip", emptyBody, "password")
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
assert.Contains(t, w.Body.String(), "Ip 91.121.79.195 performed 'crowdsecurity/ssh-bf' (6 events over ")
|
|
assert.Contains(t, w.Body.String(), `scope":"Ip","simulated":false,"type":"ban","value":"91.121.79.195"`)
|
|
|
|
// test scope (bad value)
|
|
|
|
w = lapi.RecordResponse(t, ctx, "GET", "/v1/alerts?scope=rarara", emptyBody, "password")
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
assert.Equal(t, "null", w.Body.String())
|
|
|
|
// test scenario (ok)
|
|
|
|
w = lapi.RecordResponse(t, ctx, "GET", "/v1/alerts?scenario=crowdsecurity/ssh-bf", emptyBody, "password")
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
assert.Contains(t, w.Body.String(), "Ip 91.121.79.195 performed 'crowdsecurity/ssh-bf' (6 events over ")
|
|
assert.Contains(t, w.Body.String(), `scope":"Ip","simulated":false,"type":"ban","value":"91.121.79.195"`)
|
|
|
|
// test scenario (bad value)
|
|
|
|
w = lapi.RecordResponse(t, ctx, "GET", "/v1/alerts?scenario=crowdsecurity/nope", emptyBody, "password")
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
assert.Equal(t, "null", w.Body.String())
|
|
|
|
// test ip (ok)
|
|
|
|
w = lapi.RecordResponse(t, ctx, "GET", "/v1/alerts?ip=91.121.79.195", emptyBody, "password")
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
assert.Contains(t, w.Body.String(), "Ip 91.121.79.195 performed 'crowdsecurity/ssh-bf' (6 events over ")
|
|
assert.Contains(t, w.Body.String(), `scope":"Ip","simulated":false,"type":"ban","value":"91.121.79.195"`)
|
|
|
|
// test ip (bad value)
|
|
|
|
w = lapi.RecordResponse(t, ctx, "GET", "/v1/alerts?ip=99.122.77.195", emptyBody, "password")
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
assert.Equal(t, "null", w.Body.String())
|
|
|
|
// test ip (invalid value)
|
|
|
|
w = lapi.RecordResponse(t, ctx, "GET", "/v1/alerts?ip=gruueq", emptyBody, "password")
|
|
assert.Equal(t, http.StatusInternalServerError, w.Code)
|
|
assert.JSONEq(t, `{"message":"invalid ip address 'gruueq'"}`, w.Body.String())
|
|
|
|
// test range (ok)
|
|
|
|
w = lapi.RecordResponse(t, ctx, "GET", "/v1/alerts?range=91.121.79.0/24&contains=false", emptyBody, "password")
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
assert.Contains(t, w.Body.String(), "Ip 91.121.79.195 performed 'crowdsecurity/ssh-bf' (6 events over ")
|
|
assert.Contains(t, w.Body.String(), `scope":"Ip","simulated":false,"type":"ban","value":"91.121.79.195"`)
|
|
|
|
// test range
|
|
|
|
w = lapi.RecordResponse(t, ctx, "GET", "/v1/alerts?range=99.122.77.0/24&contains=false", emptyBody, "password")
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
assert.Equal(t, "null", w.Body.String())
|
|
|
|
// test range (invalid value)
|
|
|
|
w = lapi.RecordResponse(t, ctx, "GET", "/v1/alerts?range=ratata", emptyBody, "password")
|
|
assert.Equal(t, http.StatusInternalServerError, w.Code)
|
|
assert.JSONEq(t, `{"message":"invalid ip address 'ratata'"}`, w.Body.String())
|
|
|
|
// test since (ok)
|
|
|
|
w = lapi.RecordResponse(t, ctx, "GET", "/v1/alerts?since=1h", emptyBody, "password")
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
assert.Contains(t, w.Body.String(), "Ip 91.121.79.195 performed 'crowdsecurity/ssh-bf' (6 events over ")
|
|
assert.Contains(t, w.Body.String(), `scope":"Ip","simulated":false,"type":"ban","value":"91.121.79.195"`)
|
|
|
|
// test since (ok but yields no results)
|
|
|
|
w = lapi.RecordResponse(t, ctx, "GET", "/v1/alerts?since=1ns", emptyBody, "password")
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
assert.Equal(t, "null", w.Body.String())
|
|
|
|
// test since (invalid value)
|
|
|
|
w = lapi.RecordResponse(t, ctx, "GET", "/v1/alerts?since=1zuzu", emptyBody, "password")
|
|
assert.Equal(t, http.StatusInternalServerError, w.Code)
|
|
assert.Contains(t, w.Body.String(), `{"message":"while parsing duration: time: unknown unit`)
|
|
|
|
// test until (ok)
|
|
|
|
w = lapi.RecordResponse(t, ctx, "GET", "/v1/alerts?until=1ns", emptyBody, "password")
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
assert.Contains(t, w.Body.String(), "Ip 91.121.79.195 performed 'crowdsecurity/ssh-bf' (6 events over ")
|
|
assert.Contains(t, w.Body.String(), `scope":"Ip","simulated":false,"type":"ban","value":"91.121.79.195"`)
|
|
|
|
// test until (ok but no return)
|
|
|
|
w = lapi.RecordResponse(t, ctx, "GET", "/v1/alerts?until=1m", emptyBody, "password")
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
assert.Equal(t, "null", w.Body.String())
|
|
|
|
// test until (invalid value)
|
|
|
|
w = lapi.RecordResponse(t, ctx, "GET", "/v1/alerts?until=1zuzu", emptyBody, "password")
|
|
assert.Equal(t, http.StatusInternalServerError, w.Code)
|
|
assert.Contains(t, w.Body.String(), `{"message":"while parsing duration: time: unknown unit`)
|
|
|
|
// test simulated (ok)
|
|
|
|
w = lapi.RecordResponse(t, ctx, "GET", "/v1/alerts?simulated=true", emptyBody, "password")
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
assert.Contains(t, w.Body.String(), "Ip 91.121.79.195 performed 'crowdsecurity/ssh-bf' (6 events over ")
|
|
assert.Contains(t, w.Body.String(), `scope":"Ip","simulated":false,"type":"ban","value":"91.121.79.195"`)
|
|
|
|
// test simulated (ok)
|
|
|
|
w = lapi.RecordResponse(t, ctx, "GET", "/v1/alerts?simulated=false", emptyBody, "password")
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
assert.Contains(t, w.Body.String(), "Ip 91.121.79.195 performed 'crowdsecurity/ssh-bf' (6 events over ")
|
|
assert.Contains(t, w.Body.String(), `scope":"Ip","simulated":false,"type":"ban","value":"91.121.79.195"`)
|
|
|
|
// test has active decision
|
|
|
|
w = lapi.RecordResponse(t, ctx, "GET", "/v1/alerts?has_active_decision=true", emptyBody, "password")
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
assert.Contains(t, w.Body.String(), "Ip 91.121.79.195 performed 'crowdsecurity/ssh-bf' (6 events over ")
|
|
assert.Contains(t, w.Body.String(), `scope":"Ip","simulated":false,"type":"ban","value":"91.121.79.195"`)
|
|
|
|
// test has active decision
|
|
|
|
w = lapi.RecordResponse(t, ctx, "GET", "/v1/alerts?has_active_decision=false", emptyBody, "password")
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
assert.Equal(t, "null", w.Body.String())
|
|
|
|
// test has active decision (invalid value)
|
|
|
|
w = lapi.RecordResponse(t, ctx, "GET", "/v1/alerts?has_active_decision=ratatqata", emptyBody, "password")
|
|
assert.Equal(t, http.StatusInternalServerError, w.Code)
|
|
assert.JSONEq(t, `{"message":"'ratatqata' is not a boolean: strconv.ParseBool: parsing \"ratatqata\": invalid syntax: unable to parse type"}`, w.Body.String())
|
|
}
|
|
|
|
func TestAlertBulkInsert(t *testing.T) {
|
|
ctx := t.Context()
|
|
lapi := SetupLAPITest(t, ctx)
|
|
// insert a bulk of 20 alerts to trigger bulk insert
|
|
lapi.InsertAlertFromFile(t, ctx, "./tests/alert_bulk.json")
|
|
alertContent := GetAlertReaderFromFile(t, "./tests/alert_bulk.json")
|
|
|
|
w := lapi.RecordResponse(t, ctx, "GET", "/v1/alerts", alertContent, "password")
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
}
|
|
|
|
func TestListAlert(t *testing.T) {
|
|
ctx := t.Context()
|
|
lapi := SetupLAPITest(t, ctx)
|
|
lapi.InsertAlertFromFile(t, ctx, "./tests/alert_sample.json")
|
|
// List Alert with invalid filter
|
|
|
|
w := lapi.RecordResponse(t, ctx, "GET", "/v1/alerts?test=test", emptyBody, "password")
|
|
assert.Equal(t, http.StatusInternalServerError, w.Code)
|
|
assert.JSONEq(t, `{"message":"Filter parameter 'test' is unknown (=test): invalid filter"}`, w.Body.String())
|
|
|
|
// List Alert
|
|
|
|
w = lapi.RecordResponse(t, ctx, "GET", "/v1/alerts", emptyBody, "password")
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
assert.Contains(t, w.Body.String(), "crowdsecurity/test")
|
|
}
|
|
|
|
func TestCreateAlertErrors(t *testing.T) {
|
|
ctx := t.Context()
|
|
lapi := SetupLAPITest(t, ctx)
|
|
alertContent := GetAlertReaderFromFile(t, "./tests/alert_sample.json")
|
|
|
|
// test invalid bearer
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequestWithContext(ctx, http.MethodPost, "/v1/alerts", alertContent)
|
|
req.Header.Add("User-Agent", UserAgent)
|
|
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", "ratata"))
|
|
lapi.router.ServeHTTP(w, req)
|
|
assert.Equal(t, http.StatusUnauthorized, w.Code)
|
|
|
|
// test invalid bearer
|
|
w = httptest.NewRecorder()
|
|
req, _ = http.NewRequestWithContext(ctx, http.MethodPost, "/v1/alerts", alertContent)
|
|
req.Header.Add("User-Agent", UserAgent)
|
|
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", lapi.loginResp.Token+"s"))
|
|
lapi.router.ServeHTTP(w, req)
|
|
assert.Equal(t, http.StatusUnauthorized, w.Code)
|
|
}
|
|
|
|
func TestDeleteAlert(t *testing.T) {
|
|
ctx := t.Context()
|
|
lapi := SetupLAPITest(t, ctx)
|
|
lapi.InsertAlertFromFile(t, ctx, "./tests/alert_sample.json")
|
|
|
|
// Fail Delete Alert
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequestWithContext(ctx, http.MethodDelete, "/v1/alerts", strings.NewReader(""))
|
|
AddAuthHeaders(req, lapi.loginResp)
|
|
req.RemoteAddr = "127.0.0.2:4242"
|
|
lapi.router.ServeHTTP(w, req)
|
|
assert.Equal(t, http.StatusForbidden, w.Code)
|
|
assert.JSONEq(t, `{"message":"access forbidden from this IP (127.0.0.2)"}`, w.Body.String())
|
|
|
|
// Delete Alert
|
|
w = httptest.NewRecorder()
|
|
req, _ = http.NewRequestWithContext(ctx, http.MethodDelete, "/v1/alerts", strings.NewReader(""))
|
|
AddAuthHeaders(req, lapi.loginResp)
|
|
req.RemoteAddr = "127.0.0.1:4242"
|
|
lapi.router.ServeHTTP(w, req)
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
assert.JSONEq(t, `{"nbDeleted":"1"}`, w.Body.String())
|
|
}
|
|
|
|
func TestDeleteAlertByID(t *testing.T) {
|
|
ctx := t.Context()
|
|
lapi := SetupLAPITest(t, ctx)
|
|
lapi.InsertAlertFromFile(t, ctx, "./tests/alert_sample.json")
|
|
|
|
// Fail Delete Alert
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequestWithContext(ctx, http.MethodDelete, "/v1/alerts/1", strings.NewReader(""))
|
|
AddAuthHeaders(req, lapi.loginResp)
|
|
req.RemoteAddr = "127.0.0.2:4242"
|
|
lapi.router.ServeHTTP(w, req)
|
|
assert.Equal(t, http.StatusForbidden, w.Code)
|
|
assert.JSONEq(t, `{"message":"access forbidden from this IP (127.0.0.2)"}`, w.Body.String())
|
|
|
|
// Delete Alert
|
|
w = httptest.NewRecorder()
|
|
req, _ = http.NewRequestWithContext(ctx, http.MethodDelete, "/v1/alerts/1", strings.NewReader(""))
|
|
AddAuthHeaders(req, lapi.loginResp)
|
|
req.RemoteAddr = "127.0.0.1:4242"
|
|
lapi.router.ServeHTTP(w, req)
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
assert.JSONEq(t, `{"nbDeleted":"1"}`, w.Body.String())
|
|
}
|
|
|
|
func TestDeleteAlertTrustedIPS(t *testing.T) {
|
|
ctx := t.Context()
|
|
cfg := LoadTestConfig(t)
|
|
// IPv6 mocking doesn't seem to work.
|
|
// cfg.API.Server.TrustedIPs = []string{"1.2.3.4", "1.2.4.0/24", "::"}
|
|
cfg.API.Server.TrustedIPs = []string{"1.2.3.4", "1.2.4.0/24"}
|
|
cfg.API.Server.ListenURI = "::8080"
|
|
server, err := NewServer(ctx, cfg.API.Server)
|
|
require.NoError(t, err)
|
|
|
|
err = server.InitController()
|
|
require.NoError(t, err)
|
|
|
|
router, err := server.Router()
|
|
require.NoError(t, err)
|
|
|
|
loginResp := LoginToTestAPI(t, ctx, router, cfg)
|
|
lapi := LAPI{
|
|
router: router,
|
|
loginResp: loginResp,
|
|
}
|
|
|
|
assertAlertDeleteFailedFromIP := func(ip string) {
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequestWithContext(ctx, http.MethodDelete, "/v1/alerts", strings.NewReader(""))
|
|
|
|
AddAuthHeaders(req, loginResp)
|
|
req.RemoteAddr = ip + ":1234"
|
|
|
|
router.ServeHTTP(w, req)
|
|
assert.Equal(t, http.StatusForbidden, w.Code)
|
|
assert.Contains(t, w.Body.String(), fmt.Sprintf(`{"message":"access forbidden from this IP (%s)"}`, ip))
|
|
}
|
|
|
|
assertAlertDeletedFromIP := func(ip string) {
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequestWithContext(ctx, http.MethodDelete, "/v1/alerts", strings.NewReader(""))
|
|
AddAuthHeaders(req, loginResp)
|
|
req.RemoteAddr = ip + ":1234"
|
|
|
|
router.ServeHTTP(w, req)
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
assert.JSONEq(t, `{"nbDeleted":"1"}`, w.Body.String())
|
|
}
|
|
|
|
lapi.InsertAlertFromFile(t, ctx, "./tests/alert_sample.json")
|
|
assertAlertDeleteFailedFromIP("4.3.2.1")
|
|
assertAlertDeletedFromIP("1.2.3.4")
|
|
|
|
lapi.InsertAlertFromFile(t, ctx, "./tests/alert_sample.json")
|
|
assertAlertDeletedFromIP("1.2.4.0")
|
|
lapi.InsertAlertFromFile(t, ctx, "./tests/alert_sample.json")
|
|
assertAlertDeletedFromIP("1.2.4.1")
|
|
lapi.InsertAlertFromFile(t, ctx, "./tests/alert_sample.json")
|
|
assertAlertDeletedFromIP("1.2.4.255")
|
|
|
|
lapi.InsertAlertFromFile(t, ctx, "./tests/alert_sample.json")
|
|
assertAlertDeletedFromIP("127.0.0.1")
|
|
}
|