2020-11-30 09:37:17 +00:00
|
|
|
package database
|
|
|
|
|
|
|
|
import (
|
2022-06-22 09:14:34 +00:00
|
|
|
"context"
|
2020-11-30 09:37:17 +00:00
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
2023-01-31 13:47:44 +00:00
|
|
|
"sort"
|
2020-11-30 09:37:17 +00:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
2023-09-04 12:21:45 +00:00
|
|
|
"github.com/mattn/go-sqlite3"
|
|
|
|
|
2023-06-22 09:31:41 +00:00
|
|
|
"github.com/davecgh/go-spew/spew"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
|
2023-09-04 12:21:45 +00:00
|
|
|
"github.com/crowdsecurity/go-cs-lib/slicetools"
|
|
|
|
|
2020-11-30 09:37:17 +00:00
|
|
|
"github.com/crowdsecurity/crowdsec/pkg/database/ent"
|
|
|
|
"github.com/crowdsecurity/crowdsec/pkg/database/ent/alert"
|
|
|
|
"github.com/crowdsecurity/crowdsec/pkg/database/ent/decision"
|
|
|
|
"github.com/crowdsecurity/crowdsec/pkg/database/ent/event"
|
|
|
|
"github.com/crowdsecurity/crowdsec/pkg/database/ent/meta"
|
2022-10-07 10:40:30 +00:00
|
|
|
"github.com/crowdsecurity/crowdsec/pkg/database/ent/predicate"
|
2020-11-30 09:37:17 +00:00
|
|
|
"github.com/crowdsecurity/crowdsec/pkg/models"
|
|
|
|
"github.com/crowdsecurity/crowdsec/pkg/types"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2021-12-15 10:39:37 +00:00
|
|
|
paginationSize = 100 // used to queryAlert to avoid 'too many SQL variable'
|
|
|
|
defaultLimit = 100 // default limit of element to returns when query alerts
|
|
|
|
bulkSize = 50 // bulk size when create alerts
|
2023-09-04 12:21:45 +00:00
|
|
|
maxLockRetries = 10 // how many times to retry a bulk operation when sqlite3.ErrBusy is encountered
|
2020-11-30 09:37:17 +00:00
|
|
|
)
|
|
|
|
|
2023-02-23 09:25:01 +00:00
|
|
|
func formatAlertCN(source models.Source) string {
|
|
|
|
cn := source.Cn
|
2020-11-30 09:37:17 +00:00
|
|
|
|
2023-02-23 09:25:01 +00:00
|
|
|
if source.AsNumber != "" {
|
|
|
|
cn += "/" + source.AsNumber
|
|
|
|
}
|
|
|
|
|
|
|
|
return cn
|
|
|
|
}
|
|
|
|
|
|
|
|
func formatAlertSource(alert *models.Alert) string {
|
2023-09-04 12:21:45 +00:00
|
|
|
if alert.Source == nil || alert.Source.Scope == nil || *alert.Source.Scope == "" {
|
2023-02-23 09:25:01 +00:00
|
|
|
return "empty source"
|
|
|
|
}
|
|
|
|
|
|
|
|
if *alert.Source.Scope == types.Ip {
|
|
|
|
ret := "ip " + *alert.Source.Value
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2023-02-23 09:25:01 +00:00
|
|
|
cn := formatAlertCN(*alert.Source)
|
|
|
|
if cn != "" {
|
|
|
|
ret += " (" + cn + ")"
|
2020-11-30 09:37:17 +00:00
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2023-02-23 09:25:01 +00:00
|
|
|
return ret
|
2020-11-30 09:37:17 +00:00
|
|
|
}
|
|
|
|
|
2023-02-23 09:25:01 +00:00
|
|
|
if *alert.Source.Scope == types.Range {
|
|
|
|
ret := "range " + *alert.Source.Value
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2023-02-23 09:25:01 +00:00
|
|
|
cn := formatAlertCN(*alert.Source)
|
|
|
|
if cn != "" {
|
|
|
|
ret += " (" + cn + ")"
|
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2023-02-23 09:25:01 +00:00
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
|
|
|
return *alert.Source.Scope + " " + *alert.Source.Value
|
|
|
|
}
|
|
|
|
|
2023-09-04 12:21:45 +00:00
|
|
|
func formatAlertAsString(machineID string, alert *models.Alert) []string {
|
2023-02-23 09:25:01 +00:00
|
|
|
src := formatAlertSource(alert)
|
|
|
|
|
2020-11-30 09:37:17 +00:00
|
|
|
/**/
|
2023-01-31 13:47:44 +00:00
|
|
|
msg := ""
|
|
|
|
if alert.Scenario != nil && *alert.Scenario != "" {
|
|
|
|
msg = *alert.Scenario
|
|
|
|
} else if alert.Message != nil && *alert.Message != "" {
|
|
|
|
msg = *alert.Message
|
2020-11-30 09:37:17 +00:00
|
|
|
} else {
|
2023-02-21 08:59:56 +00:00
|
|
|
msg = "empty scenario"
|
2020-11-30 09:37:17 +00:00
|
|
|
}
|
|
|
|
|
2023-02-21 08:59:56 +00:00
|
|
|
reason := fmt.Sprintf("%s by %s", msg, src)
|
2023-01-31 13:47:44 +00:00
|
|
|
|
2023-02-23 09:25:01 +00:00
|
|
|
if len(alert.Decisions) == 0 {
|
2023-09-04 12:21:45 +00:00
|
|
|
return []string{fmt.Sprintf("(%s) alert : %s", machineID, reason)}
|
2023-02-23 09:25:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var retStr []string
|
2023-01-31 13:47:44 +00:00
|
|
|
|
2023-09-04 12:21:45 +00:00
|
|
|
if alert.Decisions[0].Origin != nil && *alert.Decisions[0].Origin == types.CscliImportOrigin {
|
2023-09-12 09:04:56 +00:00
|
|
|
return []string{fmt.Sprintf("(%s) alert : %s", machineID, reason)}
|
2023-09-04 12:21:45 +00:00
|
|
|
}
|
|
|
|
|
2023-02-23 09:25:01 +00:00
|
|
|
for i, decisionItem := range alert.Decisions {
|
|
|
|
decision := ""
|
|
|
|
if alert.Simulated != nil && *alert.Simulated {
|
|
|
|
decision = "(simulated alert)"
|
|
|
|
} else if decisionItem.Simulated != nil && *decisionItem.Simulated {
|
|
|
|
decision = "(simulated decision)"
|
2020-11-30 09:37:17 +00:00
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2023-02-23 09:25:01 +00:00
|
|
|
if log.GetLevel() >= log.DebugLevel {
|
|
|
|
/*spew is expensive*/
|
|
|
|
log.Debugf("%s", spew.Sdump(decisionItem))
|
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2023-02-23 09:25:01 +00:00
|
|
|
if len(alert.Decisions) > 1 {
|
|
|
|
reason = fmt.Sprintf("%s for %d/%d decisions", msg, i+1, len(alert.Decisions))
|
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
|
|
|
machineIDOrigin := ""
|
|
|
|
if machineID == "" {
|
|
|
|
machineIDOrigin = *decisionItem.Origin
|
2023-02-23 09:25:01 +00:00
|
|
|
} else {
|
2023-09-04 12:21:45 +00:00
|
|
|
machineIDOrigin = fmt.Sprintf("%s/%s", machineID, *decisionItem.Origin)
|
2023-02-23 09:25:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
decision += fmt.Sprintf("%s %s on %s %s", *decisionItem.Duration,
|
|
|
|
*decisionItem.Type, *decisionItem.Scope, *decisionItem.Value)
|
|
|
|
retStr = append(retStr,
|
2023-09-04 12:21:45 +00:00
|
|
|
fmt.Sprintf("(%s) %s : %s", machineIDOrigin, reason, decision))
|
2020-11-30 09:37:17 +00:00
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2020-11-30 09:37:17 +00:00
|
|
|
return retStr
|
|
|
|
}
|
|
|
|
|
2023-01-31 13:47:44 +00:00
|
|
|
// CreateOrUpdateAlert is specific to PAPI : It checks if alert already exists, otherwise inserts it
|
|
|
|
// if alert already exists, it checks it associated decisions already exists
|
|
|
|
// if some associated decisions are missing (ie. previous insert ended up in error) it inserts them
|
|
|
|
func (c *Client) CreateOrUpdateAlert(machineID string, alertItem *models.Alert) (string, error) {
|
|
|
|
if alertItem.UUID == "" {
|
|
|
|
return "", fmt.Errorf("alert UUID is empty")
|
|
|
|
}
|
|
|
|
|
|
|
|
alerts, err := c.Ent.Alert.Query().Where(alert.UUID(alertItem.UUID)).WithDecisions().All(c.CTX)
|
|
|
|
|
|
|
|
if err != nil && !ent.IsNotFound(err) {
|
2023-07-06 08:14:45 +00:00
|
|
|
return "", fmt.Errorf("unable to query alerts for uuid %s: %w", alertItem.UUID, err)
|
2023-01-31 13:47:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//alert wasn't found, insert it (expected hotpath)
|
|
|
|
if ent.IsNotFound(err) || len(alerts) == 0 {
|
2023-09-04 12:21:45 +00:00
|
|
|
alertIDs, err := c.CreateAlert(machineID, []*models.Alert{alertItem})
|
2023-01-31 13:47:44 +00:00
|
|
|
if err != nil {
|
2023-07-06 08:14:45 +00:00
|
|
|
return "", fmt.Errorf("unable to create alert: %w", err)
|
2023-01-31 13:47:44 +00:00
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
|
|
|
return alertIDs[0], nil
|
2023-01-31 13:47:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//this should never happen
|
|
|
|
if len(alerts) > 1 {
|
|
|
|
return "", fmt.Errorf("multiple alerts found for uuid %s", alertItem.UUID)
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Infof("Alert %s already exists, checking associated decisions", alertItem.UUID)
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2023-01-31 13:47:44 +00:00
|
|
|
//alert is found, check for any missing decisions
|
2023-09-04 12:21:45 +00:00
|
|
|
|
|
|
|
newUuids := make([]string, len(alertItem.Decisions))
|
|
|
|
for i, decItem := range alertItem.Decisions {
|
|
|
|
newUuids[i] = decItem.UUID
|
2023-01-31 13:47:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
foundAlert := alerts[0]
|
2023-09-04 12:21:45 +00:00
|
|
|
foundUuids := make([]string, len(foundAlert.Edges.Decisions))
|
|
|
|
|
|
|
|
for i, decItem := range foundAlert.Edges.Decisions {
|
|
|
|
foundUuids[i] = decItem.UUID
|
2023-01-31 13:47:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
sort.Strings(foundUuids)
|
|
|
|
sort.Strings(newUuids)
|
|
|
|
|
2023-09-04 12:21:45 +00:00
|
|
|
missingUuids := []string{}
|
|
|
|
|
2023-01-31 13:47:44 +00:00
|
|
|
for idx, uuid := range newUuids {
|
|
|
|
if len(foundUuids) < idx+1 || uuid != foundUuids[idx] {
|
|
|
|
log.Warningf("Decision with uuid %s not found in alert %s", uuid, foundAlert.UUID)
|
|
|
|
missingUuids = append(missingUuids, uuid)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-23 09:25:01 +00:00
|
|
|
if len(missingUuids) == 0 {
|
|
|
|
log.Warningf("alert %s was already complete with decisions %+v", alertItem.UUID, foundUuids)
|
|
|
|
return "", nil
|
|
|
|
}
|
|
|
|
|
2023-09-04 12:21:45 +00:00
|
|
|
// add any and all missing decisions based on their uuids
|
|
|
|
// prepare missing decisions
|
2023-02-23 09:25:01 +00:00
|
|
|
missingDecisions := []*models.Decision{}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2023-02-23 09:25:01 +00:00
|
|
|
for _, uuid := range missingUuids {
|
|
|
|
for _, newDecision := range alertItem.Decisions {
|
|
|
|
if newDecision.UUID == uuid {
|
|
|
|
missingDecisions = append(missingDecisions, newDecision)
|
2023-01-31 13:47:44 +00:00
|
|
|
}
|
|
|
|
}
|
2023-02-23 09:25:01 +00:00
|
|
|
}
|
2023-01-31 13:47:44 +00:00
|
|
|
|
2023-02-23 09:25:01 +00:00
|
|
|
//add missing decisions
|
|
|
|
log.Debugf("Adding %d missing decisions to alert %s", len(missingDecisions), foundAlert.UUID)
|
2023-01-31 13:47:44 +00:00
|
|
|
|
2023-09-04 12:21:45 +00:00
|
|
|
decisionBuilders := make([]*ent.DecisionCreate, len(missingDecisions))
|
2023-01-31 13:47:44 +00:00
|
|
|
|
2023-02-23 09:25:01 +00:00
|
|
|
for i, decisionItem := range missingDecisions {
|
|
|
|
var start_ip, start_sfx, end_ip, end_sfx int64
|
|
|
|
var sz int
|
2023-01-31 13:47:44 +00:00
|
|
|
|
2023-02-23 09:25:01 +00:00
|
|
|
/*if the scope is IP or Range, convert the value to integers */
|
|
|
|
if strings.ToLower(*decisionItem.Scope) == "ip" || strings.ToLower(*decisionItem.Scope) == "range" {
|
|
|
|
sz, start_ip, start_sfx, end_ip, end_sfx, err = types.Addr2Ints(*decisionItem.Value)
|
2023-01-31 13:47:44 +00:00
|
|
|
if err != nil {
|
2023-06-21 07:22:25 +00:00
|
|
|
return "", errors.Wrapf(InvalidIPOrRange, "invalid addr/range %s : %s", *decisionItem.Value, err)
|
2023-01-31 13:47:44 +00:00
|
|
|
}
|
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2023-02-23 09:25:01 +00:00
|
|
|
decisionDuration, err := time.ParseDuration(*decisionItem.Duration)
|
2023-01-31 13:47:44 +00:00
|
|
|
if err != nil {
|
2023-02-23 09:25:01 +00:00
|
|
|
log.Warningf("invalid duration %s for decision %s", *decisionItem.Duration, decisionItem.UUID)
|
|
|
|
continue
|
2023-01-31 13:47:44 +00:00
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2023-02-23 09:25:01 +00:00
|
|
|
//use the created_at from the alert instead
|
|
|
|
alertTime, err := time.Parse(time.RFC3339, alertItem.CreatedAt)
|
2023-01-31 13:47:44 +00:00
|
|
|
if err != nil {
|
2023-02-23 09:25:01 +00:00
|
|
|
log.Errorf("unable to parse alert time %s : %s", alertItem.CreatedAt, err)
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2023-02-23 09:25:01 +00:00
|
|
|
alertTime = time.Now()
|
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2023-02-23 09:25:01 +00:00
|
|
|
decisionUntil := alertTime.UTC().Add(decisionDuration)
|
|
|
|
|
2023-09-04 12:21:45 +00:00
|
|
|
decisionBuilder := c.Ent.Decision.Create().
|
2023-02-23 09:25:01 +00:00
|
|
|
SetUntil(decisionUntil).
|
|
|
|
SetScenario(*decisionItem.Scenario).
|
|
|
|
SetType(*decisionItem.Type).
|
|
|
|
SetStartIP(start_ip).
|
|
|
|
SetStartSuffix(start_sfx).
|
|
|
|
SetEndIP(end_ip).
|
|
|
|
SetEndSuffix(end_sfx).
|
|
|
|
SetIPSize(int64(sz)).
|
|
|
|
SetValue(*decisionItem.Value).
|
|
|
|
SetScope(*decisionItem.Scope).
|
|
|
|
SetOrigin(*decisionItem.Origin).
|
|
|
|
SetSimulated(*alertItem.Simulated).
|
|
|
|
SetUUID(decisionItem.UUID)
|
|
|
|
|
2023-09-04 12:21:45 +00:00
|
|
|
decisionBuilders[i] = decisionBuilder
|
|
|
|
}
|
2023-02-23 09:25:01 +00:00
|
|
|
|
2023-09-04 12:21:45 +00:00
|
|
|
decisions := []*ent.Decision{}
|
|
|
|
|
|
|
|
builderChunks := slicetools.Chunks(decisionBuilders, c.decisionBulkSize)
|
|
|
|
|
|
|
|
for _, builderChunk := range builderChunks {
|
|
|
|
decisionsCreateRet, err := c.Ent.Decision.CreateBulk(builderChunk...).Save(c.CTX)
|
|
|
|
if err != nil {
|
|
|
|
return "", fmt.Errorf("creating alert decisions: %w", err)
|
2023-01-31 13:47:44 +00:00
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
|
|
|
decisions = append(decisions, decisionsCreateRet...)
|
2023-01-31 13:47:44 +00:00
|
|
|
}
|
|
|
|
|
2023-09-04 12:21:45 +00:00
|
|
|
//now that we bulk created missing decisions, let's update the alert
|
2023-01-31 13:47:44 +00:00
|
|
|
|
2023-09-04 12:21:45 +00:00
|
|
|
decisionChunks := slicetools.Chunks(decisions, c.decisionBulkSize)
|
2023-01-31 13:47:44 +00:00
|
|
|
|
2023-09-04 12:21:45 +00:00
|
|
|
for _, decisionChunk := range decisionChunks {
|
|
|
|
err = c.Ent.Alert.Update().Where(alert.UUID(alertItem.UUID)).AddDecisions(decisionChunk...).Exec(c.CTX)
|
2020-11-30 09:37:17 +00:00
|
|
|
if err != nil {
|
2023-09-04 12:21:45 +00:00
|
|
|
return "", fmt.Errorf("updating alert %s: %w", alertItem.UUID, err)
|
2020-11-30 09:37:17 +00:00
|
|
|
}
|
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
|
|
|
return "", nil
|
2020-11-30 09:37:17 +00:00
|
|
|
}
|
|
|
|
|
2023-02-06 13:06:14 +00:00
|
|
|
// UpdateCommunityBlocklist is called to update either the community blocklist (or other lists the user subscribed to)
|
|
|
|
// it takes care of creating the new alert with the associated decisions, and it will as well deleted the "older" overlapping decisions:
|
|
|
|
// 1st pull, you get decisions [1,2,3]. it inserts [1,2,3]
|
|
|
|
// 2nd pull, you get decisions [1,2,3,4]. it inserts [1,2,3,4] and will try to delete [1,2,3,4] with a different alert ID and same origin
|
2021-08-25 09:45:29 +00:00
|
|
|
func (c *Client) UpdateCommunityBlocklist(alertItem *models.Alert) (int, int, int, error) {
|
2021-09-02 10:34:20 +00:00
|
|
|
if alertItem == nil {
|
|
|
|
return 0, 0, 0, fmt.Errorf("nil alert")
|
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2021-09-02 10:34:20 +00:00
|
|
|
if alertItem.StartAt == nil {
|
|
|
|
return 0, 0, 0, fmt.Errorf("nil start_at")
|
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2021-08-25 09:45:29 +00:00
|
|
|
startAtTime, err := time.Parse(time.RFC3339, *alertItem.StartAt)
|
|
|
|
if err != nil {
|
|
|
|
return 0, 0, 0, errors.Wrapf(ParseTimeFail, "start_at field time '%s': %s", *alertItem.StartAt, err)
|
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2021-09-02 10:34:20 +00:00
|
|
|
if alertItem.StopAt == nil {
|
|
|
|
return 0, 0, 0, fmt.Errorf("nil stop_at")
|
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2021-08-25 09:45:29 +00:00
|
|
|
stopAtTime, err := time.Parse(time.RFC3339, *alertItem.StopAt)
|
|
|
|
if err != nil {
|
|
|
|
return 0, 0, 0, errors.Wrapf(ParseTimeFail, "stop_at field time '%s': %s", *alertItem.StopAt, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
ts, err := time.Parse(time.RFC3339, *alertItem.StopAt)
|
|
|
|
if err != nil {
|
|
|
|
c.Log.Errorf("While parsing StartAt of item %s : %s", *alertItem.StopAt, err)
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2022-01-19 13:56:05 +00:00
|
|
|
ts = time.Now().UTC()
|
2021-08-25 09:45:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
alertB := c.Ent.Alert.
|
|
|
|
Create().
|
|
|
|
SetScenario(*alertItem.Scenario).
|
|
|
|
SetMessage(*alertItem.Message).
|
|
|
|
SetEventsCount(*alertItem.EventsCount).
|
|
|
|
SetStartedAt(startAtTime).
|
|
|
|
SetStoppedAt(stopAtTime).
|
|
|
|
SetSourceScope(*alertItem.Source.Scope).
|
|
|
|
SetSourceValue(*alertItem.Source.Value).
|
|
|
|
SetSourceIp(alertItem.Source.IP).
|
|
|
|
SetSourceRange(alertItem.Source.Range).
|
|
|
|
SetSourceAsNumber(alertItem.Source.AsNumber).
|
|
|
|
SetSourceAsName(alertItem.Source.AsName).
|
|
|
|
SetSourceCountry(alertItem.Source.Cn).
|
|
|
|
SetSourceLatitude(alertItem.Source.Latitude).
|
|
|
|
SetSourceLongitude(alertItem.Source.Longitude).
|
|
|
|
SetCapacity(*alertItem.Capacity).
|
|
|
|
SetLeakSpeed(*alertItem.Leakspeed).
|
|
|
|
SetSimulated(*alertItem.Simulated).
|
|
|
|
SetScenarioVersion(*alertItem.ScenarioVersion).
|
|
|
|
SetScenarioHash(*alertItem.ScenarioHash)
|
|
|
|
|
|
|
|
alertRef, err := alertB.Save(c.CTX)
|
|
|
|
if err != nil {
|
|
|
|
return 0, 0, 0, errors.Wrapf(BulkError, "error creating alert : %s", err)
|
|
|
|
}
|
|
|
|
|
2023-02-23 09:25:01 +00:00
|
|
|
if len(alertItem.Decisions) == 0 {
|
|
|
|
return alertRef.ID, 0, 0, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
txClient, err := c.Ent.Tx(c.CTX)
|
|
|
|
if err != nil {
|
|
|
|
return 0, 0, 0, errors.Wrapf(BulkError, "error creating transaction : %s", err)
|
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2023-02-23 09:25:01 +00:00
|
|
|
DecOrigin := CapiMachineID
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2023-02-23 09:25:01 +00:00
|
|
|
if *alertItem.Decisions[0].Origin == CapiMachineID || *alertItem.Decisions[0].Origin == CapiListsMachineID {
|
|
|
|
DecOrigin = *alertItem.Decisions[0].Origin
|
|
|
|
} else {
|
|
|
|
log.Warningf("unexpected origin %s", *alertItem.Decisions[0].Origin)
|
|
|
|
}
|
|
|
|
|
|
|
|
deleted := 0
|
|
|
|
inserted := 0
|
|
|
|
|
2023-09-04 12:21:45 +00:00
|
|
|
decisionBuilders := make([]*ent.DecisionCreate, 0, len(alertItem.Decisions))
|
|
|
|
valueList := make([]string, 0, len(alertItem.Decisions))
|
|
|
|
|
|
|
|
for _, decisionItem := range alertItem.Decisions {
|
2023-02-23 09:25:01 +00:00
|
|
|
var start_ip, start_sfx, end_ip, end_sfx int64
|
|
|
|
var sz int
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2023-02-23 09:25:01 +00:00
|
|
|
if decisionItem.Duration == nil {
|
|
|
|
log.Warning("nil duration in community decision")
|
|
|
|
continue
|
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2023-02-23 09:25:01 +00:00
|
|
|
duration, err := time.ParseDuration(*decisionItem.Duration)
|
2022-10-26 08:48:17 +00:00
|
|
|
if err != nil {
|
2023-02-23 09:25:01 +00:00
|
|
|
rollbackErr := txClient.Rollback()
|
|
|
|
if rollbackErr != nil {
|
|
|
|
log.Errorf("rollback error: %s", rollbackErr)
|
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2023-02-23 09:25:01 +00:00
|
|
|
return 0, 0, 0, errors.Wrapf(ParseDurationFail, "decision duration '%+v' : %s", *decisionItem.Duration, err)
|
2022-10-26 08:48:17 +00:00
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2023-02-23 09:25:01 +00:00
|
|
|
if decisionItem.Scope == nil {
|
|
|
|
log.Warning("nil scope in community decision")
|
|
|
|
continue
|
2022-10-19 13:51:40 +00:00
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2023-02-23 09:25:01 +00:00
|
|
|
/*if the scope is IP or Range, convert the value to integers */
|
|
|
|
if strings.ToLower(*decisionItem.Scope) == "ip" || strings.ToLower(*decisionItem.Scope) == "range" {
|
|
|
|
sz, start_ip, start_sfx, end_ip, end_sfx, err = types.Addr2Ints(*decisionItem.Value)
|
2021-08-25 09:45:29 +00:00
|
|
|
if err != nil {
|
2022-10-26 08:48:17 +00:00
|
|
|
rollbackErr := txClient.Rollback()
|
|
|
|
if rollbackErr != nil {
|
|
|
|
log.Errorf("rollback error: %s", rollbackErr)
|
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2023-06-21 07:22:25 +00:00
|
|
|
return 0, 0, 0, errors.Wrapf(InvalidIPOrRange, "invalid addr/range %s : %s", *decisionItem.Value, err)
|
2021-08-25 09:45:29 +00:00
|
|
|
}
|
2023-02-23 09:25:01 +00:00
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2023-02-23 09:25:01 +00:00
|
|
|
/*bulk insert some new decisions*/
|
2023-09-04 12:21:45 +00:00
|
|
|
decisionBuilder := c.Ent.Decision.Create().
|
2023-02-23 09:25:01 +00:00
|
|
|
SetUntil(ts.Add(duration)).
|
|
|
|
SetScenario(*decisionItem.Scenario).
|
|
|
|
SetType(*decisionItem.Type).
|
|
|
|
SetStartIP(start_ip).
|
|
|
|
SetStartSuffix(start_sfx).
|
|
|
|
SetEndIP(end_ip).
|
|
|
|
SetEndSuffix(end_sfx).
|
|
|
|
SetIPSize(int64(sz)).
|
|
|
|
SetValue(*decisionItem.Value).
|
|
|
|
SetScope(*decisionItem.Scope).
|
|
|
|
SetOrigin(*decisionItem.Origin).
|
|
|
|
SetSimulated(*alertItem.Simulated).
|
2023-09-04 12:21:45 +00:00
|
|
|
SetOwner(alertRef)
|
|
|
|
|
|
|
|
decisionBuilders = append(decisionBuilders, decisionBuilder)
|
2023-02-23 09:25:01 +00:00
|
|
|
|
|
|
|
/*for bulk delete of duplicate decisions*/
|
|
|
|
if decisionItem.Value == nil {
|
|
|
|
log.Warning("nil value in community decision")
|
|
|
|
continue
|
|
|
|
}
|
2022-10-26 08:48:17 +00:00
|
|
|
|
2023-09-04 12:21:45 +00:00
|
|
|
valueList = append(valueList, *decisionItem.Value)
|
|
|
|
}
|
2023-02-23 09:25:01 +00:00
|
|
|
|
2023-09-04 12:21:45 +00:00
|
|
|
deleteChunks := slicetools.Chunks(valueList, c.decisionBulkSize)
|
2023-02-23 09:25:01 +00:00
|
|
|
|
2023-09-04 12:21:45 +00:00
|
|
|
for _, deleteChunk := range deleteChunks {
|
|
|
|
// Deleting older decisions from capi
|
2023-02-23 09:25:01 +00:00
|
|
|
deletedDecisions, err := txClient.Decision.Delete().
|
|
|
|
Where(decision.And(
|
|
|
|
decision.OriginEQ(DecOrigin),
|
|
|
|
decision.Not(decision.HasOwnerWith(alert.IDEQ(alertRef.ID))),
|
2023-09-04 12:21:45 +00:00
|
|
|
decision.ValueIn(deleteChunk...),
|
2023-02-23 09:25:01 +00:00
|
|
|
)).Exec(c.CTX)
|
2022-10-26 08:48:17 +00:00
|
|
|
if err != nil {
|
|
|
|
rollbackErr := txClient.Rollback()
|
|
|
|
if rollbackErr != nil {
|
|
|
|
log.Errorf("rollback error: %s", rollbackErr)
|
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2023-07-06 08:14:45 +00:00
|
|
|
return 0, 0, 0, fmt.Errorf("while deleting older community blocklist decisions: %w", err)
|
2023-02-23 09:25:01 +00:00
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2023-02-23 09:25:01 +00:00
|
|
|
deleted += deletedDecisions
|
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
|
|
|
builderChunks := slicetools.Chunks(decisionBuilders, c.decisionBulkSize)
|
|
|
|
|
|
|
|
for _, builderChunk := range builderChunks {
|
|
|
|
insertedDecisions, err := txClient.Decision.CreateBulk(builderChunk...).Save(c.CTX)
|
|
|
|
if err != nil {
|
|
|
|
rollbackErr := txClient.Rollback()
|
|
|
|
if rollbackErr != nil {
|
|
|
|
log.Errorf("rollback error: %s", rollbackErr)
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0, 0, 0, fmt.Errorf("while bulk creating decisions: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
inserted += len(insertedDecisions)
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Debugf("deleted %d decisions for %s vs %s", deleted, DecOrigin, *alertItem.Decisions[0].Origin)
|
|
|
|
|
2023-02-23 09:25:01 +00:00
|
|
|
err = txClient.Commit()
|
|
|
|
if err != nil {
|
|
|
|
rollbackErr := txClient.Rollback()
|
|
|
|
if rollbackErr != nil {
|
|
|
|
log.Errorf("rollback error: %s", rollbackErr)
|
2022-10-26 08:48:17 +00:00
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
|
|
|
return 0, 0, 0, fmt.Errorf("error committing transaction: %w", err)
|
2021-08-25 09:45:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return alertRef.ID, inserted, deleted, nil
|
|
|
|
}
|
|
|
|
|
2021-12-15 10:39:37 +00:00
|
|
|
|
2023-09-04 12:21:45 +00:00
|
|
|
func (c *Client) createDecisionChunk(simulated bool, stopAtTime time.Time, decisions []*models.Decision) ([]*ent.Decision, error) {
|
|
|
|
decisionCreate := make([]*ent.DecisionCreate, len(decisions))
|
2021-12-15 10:39:37 +00:00
|
|
|
|
2023-09-04 12:21:45 +00:00
|
|
|
for i, decisionItem := range decisions {
|
|
|
|
var start_ip, start_sfx, end_ip, end_sfx int64
|
|
|
|
var sz int
|
2023-01-31 13:47:44 +00:00
|
|
|
|
2023-09-04 12:21:45 +00:00
|
|
|
duration, err := time.ParseDuration(*decisionItem.Duration)
|
2023-01-31 13:47:44 +00:00
|
|
|
if err != nil {
|
2023-09-04 12:21:45 +00:00
|
|
|
return nil, errors.Wrapf(ParseDurationFail, "decision duration '%+v' : %s", *decisionItem.Duration, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
/*if the scope is IP or Range, convert the value to integers */
|
|
|
|
if strings.ToLower(*decisionItem.Scope) == "ip" || strings.ToLower(*decisionItem.Scope) == "range" {
|
|
|
|
sz, start_ip, start_sfx, end_ip, end_sfx, err = types.Addr2Ints(*decisionItem.Value)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("%s: %w", *decisionItem.Value, InvalidIPOrRange)
|
2023-01-31 13:47:44 +00:00
|
|
|
}
|
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
|
|
|
newDecision := c.Ent.Decision.Create().
|
|
|
|
SetUntil(stopAtTime.Add(duration)).
|
|
|
|
SetScenario(*decisionItem.Scenario).
|
|
|
|
SetType(*decisionItem.Type).
|
|
|
|
SetStartIP(start_ip).
|
|
|
|
SetStartSuffix(start_sfx).
|
|
|
|
SetEndIP(end_ip).
|
|
|
|
SetEndSuffix(end_sfx).
|
|
|
|
SetIPSize(int64(sz)).
|
|
|
|
SetValue(*decisionItem.Value).
|
|
|
|
SetScope(*decisionItem.Scope).
|
|
|
|
SetOrigin(*decisionItem.Origin).
|
|
|
|
SetSimulated(simulated).
|
|
|
|
SetUUID(decisionItem.UUID)
|
|
|
|
|
|
|
|
decisionCreate[i] = newDecision
|
2023-01-31 13:47:44 +00:00
|
|
|
}
|
2020-11-30 09:37:17 +00:00
|
|
|
|
2023-09-04 12:21:45 +00:00
|
|
|
ret, err := c.Ent.Decision.CreateBulk(decisionCreate...).Save(c.CTX)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (c *Client) createAlertChunk(machineID string, owner *ent.Machine, alerts []*models.Alert) ([]string, error) {
|
|
|
|
alertBuilders := make([]*ent.AlertCreate, len(alerts))
|
|
|
|
alertDecisions := make([][]*ent.Decision, len(alerts))
|
|
|
|
|
|
|
|
for i, alertItem := range alerts {
|
2020-12-02 16:15:48 +00:00
|
|
|
var metas []*ent.Meta
|
|
|
|
var events []*ent.Event
|
|
|
|
|
2020-11-30 09:37:17 +00:00
|
|
|
startAtTime, err := time.Parse(time.RFC3339, *alertItem.StartAt)
|
|
|
|
if err != nil {
|
2022-06-30 15:35:42 +00:00
|
|
|
c.Log.Errorf("CreateAlertBulk: Failed to parse startAtTime '%s', defaulting to now: %s", *alertItem.StartAt, err)
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2022-06-30 15:35:42 +00:00
|
|
|
startAtTime = time.Now().UTC()
|
2020-11-30 09:37:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
stopAtTime, err := time.Parse(time.RFC3339, *alertItem.StopAt)
|
|
|
|
if err != nil {
|
2022-06-30 15:35:42 +00:00
|
|
|
c.Log.Errorf("CreateAlertBulk: Failed to parse stopAtTime '%s', defaulting to now: %s", *alertItem.StopAt, err)
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2022-06-30 15:35:42 +00:00
|
|
|
stopAtTime = time.Now().UTC()
|
2020-11-30 09:37:17 +00:00
|
|
|
}
|
|
|
|
/*display proper alert in logs*/
|
2023-09-04 12:21:45 +00:00
|
|
|
for _, disp := range formatAlertAsString(machineID, alertItem) {
|
2021-03-12 14:10:56 +00:00
|
|
|
c.Log.Info(disp)
|
2020-11-30 09:37:17 +00:00
|
|
|
}
|
|
|
|
|
2021-08-03 13:46:10 +00:00
|
|
|
//let's track when we strip or drop data, notify outside of loop to avoid spam
|
|
|
|
stripped := false
|
|
|
|
dropped := false
|
|
|
|
|
2020-11-30 09:37:17 +00:00
|
|
|
if len(alertItem.Events) > 0 {
|
|
|
|
eventBulk := make([]*ent.EventCreate, len(alertItem.Events))
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2020-11-30 09:37:17 +00:00
|
|
|
for i, eventItem := range alertItem.Events {
|
|
|
|
ts, err := time.Parse(time.RFC3339, *eventItem.Timestamp)
|
|
|
|
if err != nil {
|
2022-06-30 15:35:42 +00:00
|
|
|
c.Log.Errorf("CreateAlertBulk: Failed to parse event timestamp '%s', defaulting to now: %s", *eventItem.Timestamp, err)
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2022-06-30 15:35:42 +00:00
|
|
|
ts = time.Now().UTC()
|
2020-11-30 09:37:17 +00:00
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2020-11-30 09:37:17 +00:00
|
|
|
marshallMetas, err := json.Marshal(eventItem.Meta)
|
|
|
|
if err != nil {
|
2023-06-27 12:29:42 +00:00
|
|
|
return nil, errors.Wrapf(MarshalFail, "event meta '%v' : %s", eventItem.Meta, err)
|
2020-11-30 09:37:17 +00:00
|
|
|
}
|
|
|
|
|
2021-08-03 13:46:10 +00:00
|
|
|
//the serialized field is too big, let's try to progressively strip it
|
|
|
|
if event.SerializedValidator(string(marshallMetas)) != nil {
|
|
|
|
stripped = true
|
|
|
|
|
|
|
|
valid := false
|
|
|
|
stripSize := 2048
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2021-08-03 13:46:10 +00:00
|
|
|
for !valid && stripSize > 0 {
|
|
|
|
for _, serializedItem := range eventItem.Meta {
|
|
|
|
if len(serializedItem.Value) > stripSize*2 {
|
|
|
|
serializedItem.Value = serializedItem.Value[:stripSize] + "<stripped>"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
marshallMetas, err = json.Marshal(eventItem.Meta)
|
|
|
|
if err != nil {
|
2023-06-27 12:29:42 +00:00
|
|
|
return nil, errors.Wrapf(MarshalFail, "event meta '%v' : %s", eventItem.Meta, err)
|
2021-08-03 13:46:10 +00:00
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2021-08-03 13:46:10 +00:00
|
|
|
if event.SerializedValidator(string(marshallMetas)) == nil {
|
|
|
|
valid = true
|
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2021-08-03 13:46:10 +00:00
|
|
|
stripSize /= 2
|
|
|
|
}
|
|
|
|
|
|
|
|
//nothing worked, drop it
|
|
|
|
if !valid {
|
|
|
|
dropped = true
|
|
|
|
stripped = false
|
|
|
|
marshallMetas = []byte("")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-30 09:37:17 +00:00
|
|
|
eventBulk[i] = c.Ent.Event.Create().
|
|
|
|
SetTime(ts).
|
|
|
|
SetSerialized(string(marshallMetas))
|
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2021-08-03 13:46:10 +00:00
|
|
|
if stripped {
|
2023-09-04 12:21:45 +00:00
|
|
|
c.Log.Warningf("stripped 'serialized' field (machine %s / scenario %s)", machineID, *alertItem.Scenario)
|
2021-08-03 13:46:10 +00:00
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2021-08-03 13:46:10 +00:00
|
|
|
if dropped {
|
2023-09-04 12:21:45 +00:00
|
|
|
c.Log.Warningf("dropped 'serialized' field (machine %s / scenario %s)", machineID, *alertItem.Scenario)
|
2021-08-03 13:46:10 +00:00
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2020-11-30 09:37:17 +00:00
|
|
|
events, err = c.Ent.Event.CreateBulk(eventBulk...).Save(c.CTX)
|
|
|
|
if err != nil {
|
2023-06-27 12:29:42 +00:00
|
|
|
return nil, errors.Wrapf(BulkError, "creating alert events: %s", err)
|
2020-11-30 09:37:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(alertItem.Meta) > 0 {
|
|
|
|
metaBulk := make([]*ent.MetaCreate, len(alertItem.Meta))
|
|
|
|
for i, metaItem := range alertItem.Meta {
|
|
|
|
metaBulk[i] = c.Ent.Meta.Create().
|
|
|
|
SetKey(metaItem.Key).
|
|
|
|
SetValue(metaItem.Value)
|
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2020-11-30 09:37:17 +00:00
|
|
|
metas, err = c.Ent.Meta.CreateBulk(metaBulk...).Save(c.CTX)
|
|
|
|
if err != nil {
|
2023-06-27 12:29:42 +00:00
|
|
|
return nil, errors.Wrapf(BulkError, "creating alert meta: %s", err)
|
2020-11-30 09:37:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-04 12:21:45 +00:00
|
|
|
decisions := []*ent.Decision{}
|
2021-12-15 10:39:37 +00:00
|
|
|
|
2023-09-04 12:21:45 +00:00
|
|
|
decisionChunks := slicetools.Chunks(alertItem.Decisions, c.decisionBulkSize)
|
|
|
|
for _, decisionChunk := range decisionChunks {
|
|
|
|
decisionRet, err := c.createDecisionChunk(*alertItem.Simulated, stopAtTime, decisionChunk)
|
2020-11-30 09:37:17 +00:00
|
|
|
if err != nil {
|
2023-09-04 12:21:45 +00:00
|
|
|
return nil, fmt.Errorf("creating alert decisions: %w", err)
|
2020-11-30 09:37:17 +00:00
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
|
|
|
decisions = append(decisions, decisionRet...)
|
2020-11-30 09:37:17 +00:00
|
|
|
}
|
2020-12-02 16:15:48 +00:00
|
|
|
|
2023-09-04 12:21:45 +00:00
|
|
|
alertBuilder := c.Ent.Alert.
|
2020-11-30 09:37:17 +00:00
|
|
|
Create().
|
|
|
|
SetScenario(*alertItem.Scenario).
|
|
|
|
SetMessage(*alertItem.Message).
|
|
|
|
SetEventsCount(*alertItem.EventsCount).
|
|
|
|
SetStartedAt(startAtTime).
|
|
|
|
SetStoppedAt(stopAtTime).
|
|
|
|
SetSourceScope(*alertItem.Source.Scope).
|
|
|
|
SetSourceValue(*alertItem.Source.Value).
|
|
|
|
SetSourceIp(alertItem.Source.IP).
|
|
|
|
SetSourceRange(alertItem.Source.Range).
|
|
|
|
SetSourceAsNumber(alertItem.Source.AsNumber).
|
|
|
|
SetSourceAsName(alertItem.Source.AsName).
|
|
|
|
SetSourceCountry(alertItem.Source.Cn).
|
|
|
|
SetSourceLatitude(alertItem.Source.Latitude).
|
|
|
|
SetSourceLongitude(alertItem.Source.Longitude).
|
|
|
|
SetCapacity(*alertItem.Capacity).
|
|
|
|
SetLeakSpeed(*alertItem.Leakspeed).
|
|
|
|
SetSimulated(*alertItem.Simulated).
|
|
|
|
SetScenarioVersion(*alertItem.ScenarioVersion).
|
|
|
|
SetScenarioHash(*alertItem.ScenarioHash).
|
2023-01-31 13:47:44 +00:00
|
|
|
SetUUID(alertItem.UUID).
|
2020-11-30 09:37:17 +00:00
|
|
|
AddEvents(events...).
|
|
|
|
AddMetas(metas...)
|
|
|
|
|
|
|
|
if owner != nil {
|
2023-09-04 12:21:45 +00:00
|
|
|
alertBuilder.SetOwner(owner)
|
2020-11-30 09:37:17 +00:00
|
|
|
}
|
|
|
|
|
2023-09-04 12:21:45 +00:00
|
|
|
alertBuilders[i] = alertBuilder
|
|
|
|
alertDecisions[i] = decisions
|
2020-11-30 09:37:17 +00:00
|
|
|
}
|
|
|
|
|
2023-09-04 12:21:45 +00:00
|
|
|
alertsCreateBulk, err := c.Ent.Alert.CreateBulk(alertBuilders...).Save(c.CTX)
|
2020-11-30 09:37:17 +00:00
|
|
|
if err != nil {
|
2023-09-04 12:21:45 +00:00
|
|
|
return nil, errors.Wrapf(BulkError, "bulk creating alert : %s", err)
|
2020-11-30 09:37:17 +00:00
|
|
|
}
|
|
|
|
|
2023-09-04 12:21:45 +00:00
|
|
|
ret := make([]string, len(alertsCreateBulk))
|
|
|
|
for i, a := range alertsCreateBulk {
|
|
|
|
ret[i] = strconv.Itoa(a.ID)
|
|
|
|
|
|
|
|
d := alertDecisions[i]
|
|
|
|
decisionsChunk := slicetools.Chunks(d, c.decisionBulkSize)
|
|
|
|
|
2021-12-16 17:26:19 +00:00
|
|
|
for _, d2 := range decisionsChunk {
|
2023-09-04 12:21:45 +00:00
|
|
|
retry := 0
|
|
|
|
|
|
|
|
for retry < maxLockRetries {
|
|
|
|
// so much for the happy path... but sqlite3 errors work differently
|
|
|
|
_, err := c.Ent.Alert.Update().Where(alert.IDEQ(a.ID)).AddDecisions(d2...).Save(c.CTX)
|
|
|
|
if err == nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
if sqliteErr, ok := err.(sqlite3.Error); ok {
|
|
|
|
if sqliteErr.Code == sqlite3.ErrBusy {
|
|
|
|
// sqlite3.Error{
|
|
|
|
// Code: 5,
|
|
|
|
// ExtendedCode: 5,
|
|
|
|
// SystemErrno: 0,
|
|
|
|
// err: "database is locked",
|
|
|
|
// }
|
|
|
|
retry++
|
|
|
|
log.Warningf("while updating decisions, sqlite3.ErrBusy: %s, retry %d of %d", err, retry, maxLockRetries)
|
|
|
|
time.Sleep(1 * time.Second)
|
|
|
|
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, fmt.Errorf("error while updating decisions: %w", err)
|
2021-12-15 10:39:37 +00:00
|
|
|
}
|
|
|
|
}
|
2020-11-30 09:37:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret, nil
|
|
|
|
}
|
|
|
|
|
2023-09-04 12:21:45 +00:00
|
|
|
|
|
|
|
func (c *Client) CreateAlert(machineID string, alertList []*models.Alert) ([]string, error) {
|
|
|
|
var owner *ent.Machine
|
|
|
|
var err error
|
|
|
|
|
|
|
|
if machineID != "" {
|
|
|
|
owner, err = c.QueryMachineByID(machineID)
|
|
|
|
if err != nil {
|
|
|
|
if errors.Cause(err) != UserNotExists {
|
|
|
|
return nil, fmt.Errorf("machine '%s': %w", machineID, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
c.Log.Debugf("CreateAlertBulk: Machine Id %s doesn't exist", machineID)
|
|
|
|
|
|
|
|
owner = nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
c.Log.Debugf("writing %d items", len(alertList))
|
|
|
|
|
|
|
|
alertChunks := slicetools.Chunks(alertList, bulkSize)
|
|
|
|
alertIDs := []string{}
|
|
|
|
|
|
|
|
for _, alertChunk := range alertChunks {
|
|
|
|
ids, err := c.createAlertChunk(machineID, owner, alertChunk)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("machine '%s': %w", machineID, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
alertIDs = append(alertIDs, ids...)
|
|
|
|
}
|
|
|
|
|
|
|
|
return alertIDs, nil
|
|
|
|
}
|
|
|
|
|
2022-10-07 10:40:30 +00:00
|
|
|
func AlertPredicatesFromFilter(filter map[string][]string) ([]predicate.Alert, error) {
|
|
|
|
predicates := make([]predicate.Alert, 0)
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2020-11-30 09:37:17 +00:00
|
|
|
var err error
|
2021-01-14 15:27:45 +00:00
|
|
|
var start_ip, start_sfx, end_ip, end_sfx int64
|
2020-11-30 09:37:17 +00:00
|
|
|
var hasActiveDecision bool
|
2021-01-14 15:27:45 +00:00
|
|
|
var ip_sz int
|
2023-09-04 12:21:45 +00:00
|
|
|
var contains = true
|
|
|
|
|
2021-01-14 15:27:45 +00:00
|
|
|
/*if contains is true, return bans that *contains* the given value (value is the inner)
|
|
|
|
else, return bans that are *contained* by the given value (value is the outer)*/
|
2020-11-30 09:37:17 +00:00
|
|
|
|
|
|
|
/*the simulated filter is a bit different : if it's not present *or* set to false, specifically exclude records with simulated to true */
|
|
|
|
if v, ok := filter["simulated"]; ok {
|
|
|
|
if v[0] == "false" {
|
2022-10-07 10:40:30 +00:00
|
|
|
predicates = append(predicates, alert.SimulatedEQ(false))
|
2020-11-30 09:37:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-11 13:31:51 +00:00
|
|
|
if _, ok := filter["origin"]; ok {
|
|
|
|
filter["include_capi"] = []string{"true"}
|
|
|
|
}
|
|
|
|
|
2020-11-30 09:37:17 +00:00
|
|
|
for param, value := range filter {
|
|
|
|
switch param {
|
2021-01-14 15:27:45 +00:00
|
|
|
case "contains":
|
|
|
|
contains, err = strconv.ParseBool(value[0])
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrapf(InvalidFilter, "invalid contains value : %s", err)
|
|
|
|
}
|
2020-11-30 09:37:17 +00:00
|
|
|
case "scope":
|
2023-09-04 12:21:45 +00:00
|
|
|
var scope = value[0]
|
2020-11-30 09:37:17 +00:00
|
|
|
if strings.ToLower(scope) == "ip" {
|
|
|
|
scope = types.Ip
|
|
|
|
} else if strings.ToLower(scope) == "range" {
|
|
|
|
scope = types.Range
|
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2022-10-07 10:40:30 +00:00
|
|
|
predicates = append(predicates, alert.SourceScopeEQ(scope))
|
2020-11-30 09:37:17 +00:00
|
|
|
case "value":
|
2022-10-07 10:40:30 +00:00
|
|
|
predicates = append(predicates, alert.SourceValueEQ(value[0]))
|
2020-11-30 09:37:17 +00:00
|
|
|
case "scenario":
|
2022-10-07 10:40:30 +00:00
|
|
|
predicates = append(predicates, alert.HasDecisionsWith(decision.ScenarioEQ(value[0])))
|
2021-01-14 15:27:45 +00:00
|
|
|
case "ip", "range":
|
|
|
|
ip_sz, start_ip, start_sfx, end_ip, end_sfx, err = types.Addr2Ints(value[0])
|
2020-11-30 09:37:17 +00:00
|
|
|
if err != nil {
|
2021-01-14 15:27:45 +00:00
|
|
|
return nil, errors.Wrapf(InvalidIPOrRange, "unable to convert '%s' to int: %s", value[0], err)
|
2020-11-30 09:37:17 +00:00
|
|
|
}
|
|
|
|
case "since":
|
2023-08-24 07:44:46 +00:00
|
|
|
duration, err := ParseDuration(value[0])
|
2020-11-30 09:37:17 +00:00
|
|
|
if err != nil {
|
2023-07-06 08:14:45 +00:00
|
|
|
return nil, fmt.Errorf("while parsing duration: %w", err)
|
2020-11-30 09:37:17 +00:00
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2022-01-19 13:56:05 +00:00
|
|
|
since := time.Now().UTC().Add(-duration)
|
2020-11-30 09:37:17 +00:00
|
|
|
if since.IsZero() {
|
2023-09-04 12:21:45 +00:00
|
|
|
return nil, fmt.Errorf("empty time now() - %s", since.String())
|
2020-11-30 09:37:17 +00:00
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2022-10-07 10:40:30 +00:00
|
|
|
predicates = append(predicates, alert.StartedAtGTE(since))
|
2020-11-30 09:37:17 +00:00
|
|
|
case "created_before":
|
2023-08-24 07:44:46 +00:00
|
|
|
duration, err := ParseDuration(value[0])
|
2020-11-30 09:37:17 +00:00
|
|
|
if err != nil {
|
2023-07-06 08:14:45 +00:00
|
|
|
return nil, fmt.Errorf("while parsing duration: %w", err)
|
2020-11-30 09:37:17 +00:00
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2022-01-19 13:56:05 +00:00
|
|
|
since := time.Now().UTC().Add(-duration)
|
2020-11-30 09:37:17 +00:00
|
|
|
if since.IsZero() {
|
2023-07-06 08:14:45 +00:00
|
|
|
return nil, fmt.Errorf("empty time now() - %s", since.String())
|
2020-11-30 09:37:17 +00:00
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2022-10-07 10:40:30 +00:00
|
|
|
predicates = append(predicates, alert.CreatedAtLTE(since))
|
2020-11-30 09:37:17 +00:00
|
|
|
case "until":
|
2023-08-24 07:44:46 +00:00
|
|
|
duration, err := ParseDuration(value[0])
|
2020-11-30 09:37:17 +00:00
|
|
|
if err != nil {
|
2023-07-06 08:14:45 +00:00
|
|
|
return nil, fmt.Errorf("while parsing duration: %w", err)
|
2020-11-30 09:37:17 +00:00
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2022-01-19 13:56:05 +00:00
|
|
|
until := time.Now().UTC().Add(-duration)
|
2020-11-30 09:37:17 +00:00
|
|
|
if until.IsZero() {
|
2023-07-06 08:14:45 +00:00
|
|
|
return nil, fmt.Errorf("empty time now() - %s", until.String())
|
2020-11-30 09:37:17 +00:00
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2022-10-07 10:40:30 +00:00
|
|
|
predicates = append(predicates, alert.StartedAtLTE(until))
|
2020-11-30 09:37:17 +00:00
|
|
|
case "decision_type":
|
2022-10-07 10:40:30 +00:00
|
|
|
predicates = append(predicates, alert.HasDecisionsWith(decision.TypeEQ(value[0])))
|
2022-01-11 13:31:51 +00:00
|
|
|
case "origin":
|
2022-10-07 10:40:30 +00:00
|
|
|
predicates = append(predicates, alert.HasDecisionsWith(decision.OriginEQ(value[0])))
|
2020-11-30 09:37:17 +00:00
|
|
|
case "include_capi": //allows to exclude one or more specific origins
|
|
|
|
if value[0] == "false" {
|
2023-09-12 09:19:36 +00:00
|
|
|
predicates = append(predicates, alert.Not(alert.HasDecisionsWith(decision.OriginEQ(types.CAPIOrigin))))
|
2020-11-30 09:37:17 +00:00
|
|
|
} else if value[0] != "true" {
|
|
|
|
log.Errorf("Invalid bool '%s' for include_capi", value[0])
|
|
|
|
}
|
|
|
|
case "has_active_decision":
|
|
|
|
if hasActiveDecision, err = strconv.ParseBool(value[0]); err != nil {
|
|
|
|
return nil, errors.Wrapf(ParseType, "'%s' is not a boolean: %s", value[0], err)
|
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2020-11-30 09:37:17 +00:00
|
|
|
if hasActiveDecision {
|
2022-10-07 10:40:30 +00:00
|
|
|
predicates = append(predicates, alert.HasDecisionsWith(decision.UntilGTE(time.Now().UTC())))
|
2020-11-30 09:37:17 +00:00
|
|
|
} else {
|
2022-10-07 10:40:30 +00:00
|
|
|
predicates = append(predicates, alert.Not(alert.HasDecisions()))
|
2020-11-30 09:37:17 +00:00
|
|
|
}
|
|
|
|
case "limit":
|
|
|
|
continue
|
|
|
|
case "sort":
|
|
|
|
continue
|
2022-04-27 09:05:40 +00:00
|
|
|
case "simulated":
|
|
|
|
continue
|
2023-05-04 11:06:15 +00:00
|
|
|
case "with_decisions":
|
|
|
|
continue
|
2020-11-30 09:37:17 +00:00
|
|
|
default:
|
|
|
|
return nil, errors.Wrapf(InvalidFilter, "Filter parameter '%s' is unknown (=%s)", param, value[0])
|
|
|
|
}
|
|
|
|
}
|
2021-01-14 15:27:45 +00:00
|
|
|
|
|
|
|
if ip_sz == 4 {
|
|
|
|
if contains { /*decision contains {start_ip,end_ip}*/
|
2022-10-07 10:40:30 +00:00
|
|
|
predicates = append(predicates, alert.And(
|
2021-01-14 15:27:45 +00:00
|
|
|
alert.HasDecisionsWith(decision.StartIPLTE(start_ip)),
|
|
|
|
alert.HasDecisionsWith(decision.EndIPGTE(end_ip)),
|
|
|
|
alert.HasDecisionsWith(decision.IPSizeEQ(int64(ip_sz))),
|
|
|
|
))
|
|
|
|
} else { /*decision is contained within {start_ip,end_ip}*/
|
2022-10-07 10:40:30 +00:00
|
|
|
predicates = append(predicates, alert.And(
|
2021-01-14 15:27:45 +00:00
|
|
|
alert.HasDecisionsWith(decision.StartIPGTE(start_ip)),
|
|
|
|
alert.HasDecisionsWith(decision.EndIPLTE(end_ip)),
|
|
|
|
alert.HasDecisionsWith(decision.IPSizeEQ(int64(ip_sz))),
|
|
|
|
))
|
|
|
|
}
|
|
|
|
} else if ip_sz == 16 {
|
|
|
|
if contains { /*decision contains {start_ip,end_ip}*/
|
2022-10-07 10:40:30 +00:00
|
|
|
predicates = append(predicates, alert.And(
|
2021-01-14 15:27:45 +00:00
|
|
|
//matching addr size
|
|
|
|
alert.HasDecisionsWith(decision.IPSizeEQ(int64(ip_sz))),
|
|
|
|
alert.Or(
|
|
|
|
//decision.start_ip < query.start_ip
|
|
|
|
alert.HasDecisionsWith(decision.StartIPLT(start_ip)),
|
|
|
|
alert.And(
|
|
|
|
//decision.start_ip == query.start_ip
|
|
|
|
alert.HasDecisionsWith(decision.StartIPEQ(start_ip)),
|
|
|
|
//decision.start_suffix <= query.start_suffix
|
|
|
|
alert.HasDecisionsWith(decision.StartSuffixLTE(start_sfx)),
|
|
|
|
)),
|
|
|
|
alert.Or(
|
|
|
|
//decision.end_ip > query.end_ip
|
|
|
|
alert.HasDecisionsWith(decision.EndIPGT(end_ip)),
|
|
|
|
alert.And(
|
|
|
|
//decision.end_ip == query.end_ip
|
|
|
|
alert.HasDecisionsWith(decision.EndIPEQ(end_ip)),
|
|
|
|
//decision.end_suffix >= query.end_suffix
|
|
|
|
alert.HasDecisionsWith(decision.EndSuffixGTE(end_sfx)),
|
|
|
|
),
|
|
|
|
),
|
2020-11-30 09:37:17 +00:00
|
|
|
))
|
2021-01-14 15:27:45 +00:00
|
|
|
} else { /*decision is contained within {start_ip,end_ip}*/
|
2022-10-07 10:40:30 +00:00
|
|
|
predicates = append(predicates, alert.And(
|
2021-01-14 15:27:45 +00:00
|
|
|
//matching addr size
|
|
|
|
alert.HasDecisionsWith(decision.IPSizeEQ(int64(ip_sz))),
|
|
|
|
alert.Or(
|
|
|
|
//decision.start_ip > query.start_ip
|
|
|
|
alert.HasDecisionsWith(decision.StartIPGT(start_ip)),
|
|
|
|
alert.And(
|
|
|
|
//decision.start_ip == query.start_ip
|
|
|
|
alert.HasDecisionsWith(decision.StartIPEQ(start_ip)),
|
|
|
|
//decision.start_suffix >= query.start_suffix
|
|
|
|
alert.HasDecisionsWith(decision.StartSuffixGTE(start_sfx)),
|
|
|
|
)),
|
|
|
|
alert.Or(
|
|
|
|
//decision.end_ip < query.end_ip
|
|
|
|
alert.HasDecisionsWith(decision.EndIPLT(end_ip)),
|
|
|
|
alert.And(
|
|
|
|
//decision.end_ip == query.end_ip
|
|
|
|
alert.HasDecisionsWith(decision.EndIPEQ(end_ip)),
|
|
|
|
//decision.end_suffix <= query.end_suffix
|
|
|
|
alert.HasDecisionsWith(decision.EndSuffixLTE(end_sfx)),
|
|
|
|
),
|
|
|
|
),
|
2020-11-30 09:37:17 +00:00
|
|
|
))
|
|
|
|
}
|
2021-01-14 15:27:45 +00:00
|
|
|
} else if ip_sz != 0 {
|
|
|
|
return nil, errors.Wrapf(InvalidFilter, "Unknown ip size %d", ip_sz)
|
2020-11-30 09:37:17 +00:00
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2022-10-07 10:40:30 +00:00
|
|
|
return predicates, nil
|
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2022-10-07 10:40:30 +00:00
|
|
|
func BuildAlertRequestFromFilter(alerts *ent.AlertQuery, filter map[string][]string) (*ent.AlertQuery, error) {
|
|
|
|
preds, err := AlertPredicatesFromFilter(filter)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2022-10-07 10:40:30 +00:00
|
|
|
return alerts.Where(preds...), nil
|
2020-11-30 09:37:17 +00:00
|
|
|
}
|
|
|
|
|
2022-06-22 09:14:34 +00:00
|
|
|
func (c *Client) AlertsCountPerScenario(filters map[string][]string) (map[string]int, error) {
|
|
|
|
var res []struct {
|
|
|
|
Scenario string
|
|
|
|
Count int
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
|
query := c.Ent.Alert.Query()
|
|
|
|
|
|
|
|
query, err := BuildAlertRequestFromFilter(query, filters)
|
|
|
|
|
|
|
|
if err != nil {
|
2023-07-06 08:14:45 +00:00
|
|
|
return nil, fmt.Errorf("failed to build alert request: %w", err)
|
2022-06-22 09:14:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
err = query.GroupBy(alert.FieldScenario).Aggregate(ent.Count()).Scan(ctx, &res)
|
|
|
|
|
|
|
|
if err != nil {
|
2023-07-06 08:14:45 +00:00
|
|
|
return nil, fmt.Errorf("failed to count alerts per scenario: %w", err)
|
2022-06-22 09:14:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
counts := make(map[string]int)
|
|
|
|
|
|
|
|
for _, r := range res {
|
|
|
|
counts[r.Scenario] = r.Count
|
|
|
|
}
|
|
|
|
|
|
|
|
return counts, nil
|
|
|
|
}
|
|
|
|
|
2020-11-30 09:37:17 +00:00
|
|
|
func (c *Client) TotalAlerts() (int, error) {
|
|
|
|
return c.Ent.Alert.Query().Count(c.CTX)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) QueryAlertWithFilter(filter map[string][]string) ([]*ent.Alert, error) {
|
|
|
|
sort := "DESC" // we sort by desc by default
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2020-11-30 09:37:17 +00:00
|
|
|
if val, ok := filter["sort"]; ok {
|
|
|
|
if val[0] != "ASC" && val[0] != "DESC" {
|
2021-03-12 14:10:56 +00:00
|
|
|
c.Log.Errorf("invalid 'sort' parameter: %s", val)
|
2020-11-30 09:37:17 +00:00
|
|
|
} else {
|
|
|
|
sort = val[0]
|
|
|
|
}
|
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2020-11-30 09:37:17 +00:00
|
|
|
limit := defaultLimit
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2020-11-30 09:37:17 +00:00
|
|
|
if val, ok := filter["limit"]; ok {
|
|
|
|
limitConv, err := strconv.Atoi(val[0])
|
|
|
|
if err != nil {
|
2023-09-04 12:21:45 +00:00
|
|
|
return nil, errors.Wrapf(QueryFail, "bad limit in parameters: %s", val)
|
2020-11-30 09:37:17 +00:00
|
|
|
}
|
|
|
|
|
2023-09-04 12:21:45 +00:00
|
|
|
limit = limitConv
|
2020-11-30 09:37:17 +00:00
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2020-11-30 09:37:17 +00:00
|
|
|
offset := 0
|
|
|
|
ret := make([]*ent.Alert, 0)
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2020-11-30 09:37:17 +00:00
|
|
|
for {
|
|
|
|
alerts := c.Ent.Alert.Query()
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2020-11-30 09:37:17 +00:00
|
|
|
alerts, err := BuildAlertRequestFromFilter(alerts, filter)
|
|
|
|
if err != nil {
|
2023-09-04 12:21:45 +00:00
|
|
|
return nil, err
|
2020-11-30 09:37:17 +00:00
|
|
|
}
|
2023-04-14 09:39:16 +00:00
|
|
|
|
|
|
|
//only if with_decisions is present and set to false, we exclude this
|
|
|
|
if val, ok := filter["with_decisions"]; ok && val[0] == "false" {
|
|
|
|
c.Log.Debugf("skipping decisions")
|
|
|
|
} else {
|
|
|
|
alerts = alerts.
|
|
|
|
WithDecisions()
|
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2020-11-30 09:37:17 +00:00
|
|
|
alerts = alerts.
|
|
|
|
WithEvents().
|
|
|
|
WithMetas().
|
|
|
|
WithOwner()
|
2022-01-19 13:50:53 +00:00
|
|
|
|
2020-11-30 09:37:17 +00:00
|
|
|
if limit == 0 {
|
|
|
|
limit, err = alerts.Count(c.CTX)
|
|
|
|
if err != nil {
|
2023-09-04 12:21:45 +00:00
|
|
|
return nil, fmt.Errorf("unable to count nb alerts: %s", err)
|
2020-11-30 09:37:17 +00:00
|
|
|
}
|
|
|
|
}
|
2022-01-19 13:50:53 +00:00
|
|
|
|
|
|
|
if sort == "ASC" {
|
2022-05-27 13:23:59 +00:00
|
|
|
alerts = alerts.Order(ent.Asc(alert.FieldCreatedAt), ent.Asc(alert.FieldID))
|
2022-01-19 13:50:53 +00:00
|
|
|
} else {
|
2022-05-27 13:23:59 +00:00
|
|
|
alerts = alerts.Order(ent.Desc(alert.FieldCreatedAt), ent.Desc(alert.FieldID))
|
2022-01-19 13:50:53 +00:00
|
|
|
}
|
|
|
|
|
2020-11-30 09:37:17 +00:00
|
|
|
result, err := alerts.Limit(paginationSize).Offset(offset).All(c.CTX)
|
|
|
|
if err != nil {
|
2023-09-04 12:21:45 +00:00
|
|
|
return nil, errors.Wrapf(QueryFail, "pagination size: %d, offset: %d: %s", paginationSize, offset, err)
|
2020-11-30 09:37:17 +00:00
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2020-11-30 09:37:17 +00:00
|
|
|
if diff := limit - len(ret); diff < paginationSize {
|
|
|
|
if len(result) < diff {
|
|
|
|
ret = append(ret, result...)
|
2021-03-12 14:10:56 +00:00
|
|
|
c.Log.Debugf("Pagination done, %d < %d", len(result), diff)
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2020-11-30 09:37:17 +00:00
|
|
|
break
|
|
|
|
}
|
2022-04-27 09:05:40 +00:00
|
|
|
|
2023-09-04 12:21:45 +00:00
|
|
|
ret = append(ret, result[0:diff]...)
|
2020-11-30 09:37:17 +00:00
|
|
|
} else {
|
|
|
|
ret = append(ret, result...)
|
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2022-04-27 09:05:40 +00:00
|
|
|
if len(ret) == limit || len(ret) == 0 || len(ret) < paginationSize {
|
2021-03-12 14:10:56 +00:00
|
|
|
c.Log.Debugf("Pagination done len(ret) = %d", len(ret))
|
2020-11-30 09:37:17 +00:00
|
|
|
break
|
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2020-11-30 09:37:17 +00:00
|
|
|
offset += paginationSize
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret, nil
|
|
|
|
}
|
|
|
|
|
2021-10-26 11:33:45 +00:00
|
|
|
func (c *Client) DeleteAlertGraphBatch(alertItems []*ent.Alert) (int, error) {
|
|
|
|
idList := make([]int, 0)
|
|
|
|
for _, alert := range alertItems {
|
2022-06-16 12:41:54 +00:00
|
|
|
idList = append(idList, alert.ID)
|
2021-10-26 11:33:45 +00:00
|
|
|
}
|
|
|
|
|
2022-10-05 15:07:44 +00:00
|
|
|
_, err := c.Ent.Event.Delete().
|
|
|
|
Where(event.HasOwnerWith(alert.IDIn(idList...))).Exec(c.CTX)
|
|
|
|
if err != nil {
|
|
|
|
c.Log.Warningf("DeleteAlertGraphBatch : %s", err)
|
|
|
|
return 0, errors.Wrapf(DeleteFail, "alert graph delete batch events")
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = c.Ent.Meta.Delete().
|
|
|
|
Where(meta.HasOwnerWith(alert.IDIn(idList...))).Exec(c.CTX)
|
|
|
|
if err != nil {
|
|
|
|
c.Log.Warningf("DeleteAlertGraphBatch : %s", err)
|
|
|
|
return 0, errors.Wrapf(DeleteFail, "alert graph delete batch meta")
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = c.Ent.Decision.Delete().
|
|
|
|
Where(decision.HasOwnerWith(alert.IDIn(idList...))).Exec(c.CTX)
|
|
|
|
if err != nil {
|
|
|
|
c.Log.Warningf("DeleteAlertGraphBatch : %s", err)
|
|
|
|
return 0, errors.Wrapf(DeleteFail, "alert graph delete batch decisions")
|
|
|
|
}
|
|
|
|
|
2021-10-26 11:33:45 +00:00
|
|
|
deleted, err := c.Ent.Alert.Delete().
|
|
|
|
Where(alert.IDIn(idList...)).Exec(c.CTX)
|
|
|
|
if err != nil {
|
2022-10-05 15:07:44 +00:00
|
|
|
c.Log.Warningf("DeleteAlertGraphBatch : %s", err)
|
2021-10-26 11:33:45 +00:00
|
|
|
return deleted, errors.Wrapf(DeleteFail, "alert graph delete batch")
|
|
|
|
}
|
|
|
|
|
|
|
|
c.Log.Debug("Done batch delete alerts")
|
|
|
|
|
|
|
|
return deleted, nil
|
|
|
|
}
|
|
|
|
|
2020-11-30 09:37:17 +00:00
|
|
|
func (c *Client) DeleteAlertGraph(alertItem *ent.Alert) error {
|
|
|
|
// delete the associated events
|
|
|
|
_, err := c.Ent.Event.Delete().
|
|
|
|
Where(event.HasOwnerWith(alert.IDEQ(alertItem.ID))).Exec(c.CTX)
|
|
|
|
if err != nil {
|
2021-03-12 14:10:56 +00:00
|
|
|
c.Log.Warningf("DeleteAlertGraph : %s", err)
|
2020-11-30 09:37:17 +00:00
|
|
|
return errors.Wrapf(DeleteFail, "event with alert ID '%d'", alertItem.ID)
|
|
|
|
}
|
|
|
|
|
|
|
|
// delete the associated meta
|
|
|
|
_, err = c.Ent.Meta.Delete().
|
|
|
|
Where(meta.HasOwnerWith(alert.IDEQ(alertItem.ID))).Exec(c.CTX)
|
|
|
|
if err != nil {
|
2021-03-12 14:10:56 +00:00
|
|
|
c.Log.Warningf("DeleteAlertGraph : %s", err)
|
2020-11-30 09:37:17 +00:00
|
|
|
return errors.Wrapf(DeleteFail, "meta with alert ID '%d'", alertItem.ID)
|
|
|
|
}
|
|
|
|
|
|
|
|
// delete the associated decisions
|
|
|
|
_, err = c.Ent.Decision.Delete().
|
|
|
|
Where(decision.HasOwnerWith(alert.IDEQ(alertItem.ID))).Exec(c.CTX)
|
|
|
|
if err != nil {
|
2021-03-12 14:10:56 +00:00
|
|
|
c.Log.Warningf("DeleteAlertGraph : %s", err)
|
2020-11-30 09:37:17 +00:00
|
|
|
return errors.Wrapf(DeleteFail, "decision with alert ID '%d'", alertItem.ID)
|
|
|
|
}
|
|
|
|
|
|
|
|
// delete the alert
|
|
|
|
err = c.Ent.Alert.DeleteOne(alertItem).Exec(c.CTX)
|
|
|
|
if err != nil {
|
2021-03-12 14:10:56 +00:00
|
|
|
c.Log.Warningf("DeleteAlertGraph : %s", err)
|
2020-11-30 09:37:17 +00:00
|
|
|
return errors.Wrapf(DeleteFail, "alert with ID '%d'", alertItem.ID)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-10-19 12:37:27 +00:00
|
|
|
func (c *Client) DeleteAlertByID(id int) error {
|
|
|
|
alertItem, err := c.Ent.Alert.Query().Where(alert.IDEQ(id)).Only(c.CTX)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return c.DeleteAlertGraph(alertItem)
|
|
|
|
}
|
|
|
|
|
2020-11-30 15:15:07 +00:00
|
|
|
func (c *Client) DeleteAlertWithFilter(filter map[string][]string) (int, error) {
|
2022-10-07 10:40:30 +00:00
|
|
|
preds, err := AlertPredicatesFromFilter(filter)
|
2022-03-09 15:15:18 +00:00
|
|
|
if err != nil {
|
2022-10-07 10:40:30 +00:00
|
|
|
return 0, err
|
2022-03-09 15:15:18 +00:00
|
|
|
}
|
2022-06-08 14:05:52 +00:00
|
|
|
|
2023-09-04 12:21:45 +00:00
|
|
|
return c.Ent.Alert.Delete().Where(preds...).Exec(c.CTX)
|
2020-11-30 09:37:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) GetAlertByID(alertID int) (*ent.Alert, error) {
|
|
|
|
alert, err := c.Ent.Alert.Query().Where(alert.IDEQ(alertID)).WithDecisions().WithEvents().WithMetas().WithOwner().First(c.CTX)
|
|
|
|
if err != nil {
|
2020-11-30 15:15:07 +00:00
|
|
|
/*record not found, 404*/
|
|
|
|
if ent.IsNotFound(err) {
|
|
|
|
log.Warningf("GetAlertByID (not found): %s", err)
|
|
|
|
return &ent.Alert{}, ItemNotFound
|
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2021-03-12 14:10:56 +00:00
|
|
|
c.Log.Warningf("GetAlertByID : %s", err)
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2020-11-30 15:15:07 +00:00
|
|
|
return &ent.Alert{}, QueryFail
|
2020-11-30 09:37:17 +00:00
|
|
|
}
|
2023-09-04 12:21:45 +00:00
|
|
|
|
2020-11-30 09:37:17 +00:00
|
|
|
return alert, nil
|
|
|
|
}
|