bitbucket.org/Aishee/synsec@v0.0.0-20210414005726-236fc01a153d/pkg/apiserver/controllers/v1/alerts.go (about)

     1  package v1
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"net/http"
     7  	"strconv"
     8  	"time"
     9  
    10  	jwt "github.com/appleboy/gin-jwt/v2"
    11  
    12  	"bitbucket.org/Aishee/synsec/pkg/csprofiles"
    13  	"bitbucket.org/Aishee/synsec/pkg/database/ent"
    14  	"bitbucket.org/Aishee/synsec/pkg/models"
    15  	"bitbucket.org/Aishee/synsec/pkg/types"
    16  	"github.com/gin-gonic/gin"
    17  	"github.com/go-openapi/strfmt"
    18  	log "github.com/sirupsen/logrus"
    19  )
    20  
    21  func FormatOneAlert(alert *ent.Alert) *models.Alert {
    22  	var outputAlert models.Alert
    23  	var machineID string
    24  	startAt := alert.StartedAt.String()
    25  	StopAt := alert.StoppedAt.String()
    26  	if alert.Edges.Owner == nil {
    27  		machineID = "N/A"
    28  	} else {
    29  		machineID = alert.Edges.Owner.MachineId
    30  	}
    31  
    32  	outputAlert = models.Alert{
    33  		ID:              int64(alert.ID),
    34  		MachineID:       machineID,
    35  		CreatedAt:       alert.CreatedAt.Format(time.RFC3339),
    36  		Scenario:        &alert.Scenario,
    37  		ScenarioVersion: &alert.ScenarioVersion,
    38  		ScenarioHash:    &alert.ScenarioHash,
    39  		Message:         &alert.Message,
    40  		EventsCount:     &alert.EventsCount,
    41  		StartAt:         &startAt,
    42  		StopAt:          &StopAt,
    43  		Capacity:        &alert.Capacity,
    44  		Leakspeed:       &alert.LeakSpeed,
    45  		Simulated:       &alert.Simulated,
    46  		Source: &models.Source{
    47  			Scope:     &alert.SourceScope,
    48  			Value:     &alert.SourceValue,
    49  			IP:        alert.SourceIp,
    50  			Range:     alert.SourceRange,
    51  			AsNumber:  alert.SourceAsNumber,
    52  			AsName:    alert.SourceAsName,
    53  			Cn:        alert.SourceCountry,
    54  			Latitude:  alert.SourceLatitude,
    55  			Longitude: alert.SourceLongitude,
    56  		},
    57  	}
    58  	for _, eventItem := range alert.Edges.Events {
    59  		var Metas models.Meta
    60  		timestamp := eventItem.Time.String()
    61  		if err := json.Unmarshal([]byte(eventItem.Serialized), &Metas); err != nil {
    62  			log.Errorf("unable to unmarshall events meta '%s' : %s", eventItem.Serialized, err)
    63  		}
    64  		outputAlert.Events = append(outputAlert.Events, &models.Event{
    65  			Timestamp: &timestamp,
    66  			Meta:      Metas,
    67  		})
    68  	}
    69  	for _, metaItem := range alert.Edges.Metas {
    70  		outputAlert.Meta = append(outputAlert.Meta, &models.MetaItems0{
    71  			Key:   metaItem.Key,
    72  			Value: metaItem.Value,
    73  		})
    74  	}
    75  	for _, decisionItem := range alert.Edges.Decisions {
    76  		duration := decisionItem.Until.Sub(time.Now()).String()
    77  		outputAlert.Decisions = append(outputAlert.Decisions, &models.Decision{
    78  			Duration:  &duration, // transform into time.Time ?
    79  			Scenario:  &decisionItem.Scenario,
    80  			Type:      &decisionItem.Type,
    81  			Scope:     &decisionItem.Scope,
    82  			Value:     &decisionItem.Value,
    83  			Origin:    &decisionItem.Origin,
    84  			Simulated: outputAlert.Simulated,
    85  			ID:        int64(decisionItem.ID),
    86  		})
    87  	}
    88  	return &outputAlert
    89  }
    90  
    91  // FormatAlerts : Format results from the database to be swagger model compliant
    92  func FormatAlerts(result []*ent.Alert) models.AddAlertsRequest {
    93  	var data models.AddAlertsRequest
    94  	for _, alertItem := range result {
    95  		data = append(data, FormatOneAlert(alertItem))
    96  	}
    97  	return data
    98  }
    99  
   100  // CreateAlert : write received alerts in body to the database
   101  func (c *Controller) CreateAlert(gctx *gin.Context) {
   102  	defer types.CatchPanic("synsec/controllersV1/CreateAlert")
   103  
   104  	var input models.AddAlertsRequest
   105  
   106  	claims := jwt.ExtractClaims(gctx)
   107  	/*TBD : use defines rather than hardcoded key to find back owner*/
   108  	machineID := claims["id"].(string)
   109  
   110  	if err := gctx.ShouldBindJSON(&input); err != nil {
   111  		gctx.JSON(http.StatusBadRequest, gin.H{"message": err.Error()})
   112  		return
   113  	}
   114  	if err := input.Validate(strfmt.Default); err != nil {
   115  		c.HandleDBErrors(gctx, err)
   116  		return
   117  	}
   118  
   119  	for _, alert := range input {
   120  		if len(alert.Decisions) == 0 {
   121  			decisions, err := csprofiles.EvaluateProfiles(c.Profiles, alert)
   122  			if err != nil {
   123  				gctx.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()})
   124  				return
   125  			}
   126  			alert.Decisions = decisions
   127  		}
   128  	}
   129  
   130  	alerts, err := c.DBClient.CreateAlert(machineID, input)
   131  	if err != nil {
   132  		c.HandleDBErrors(gctx, err)
   133  		return
   134  	}
   135  	for _, alert := range input {
   136  		alert.MachineID = machineID
   137  	}
   138  	select {
   139  	case c.CAPIChan <- input:
   140  		log.Debugf("alert send to CAPI channel")
   141  	default:
   142  		log.Warningf("Cannot send alert to Central API channel")
   143  	}
   144  
   145  	gctx.JSON(http.StatusCreated, alerts)
   146  	return
   147  }
   148  
   149  // FindAlerts : return alerts from database based on the specified filter
   150  func (c *Controller) FindAlerts(gctx *gin.Context) {
   151  	defer types.CatchPanic("synsec/controllersV1/FindAlerts")
   152  	result, err := c.DBClient.QueryAlertWithFilter(gctx.Request.URL.Query())
   153  	if err != nil {
   154  		c.HandleDBErrors(gctx, err)
   155  		return
   156  	}
   157  	data := FormatAlerts(result)
   158  
   159  	if gctx.Request.Method == "HEAD" {
   160  		gctx.String(http.StatusOK, "")
   161  		return
   162  	}
   163  	gctx.JSON(http.StatusOK, data)
   164  	return
   165  }
   166  
   167  // FindAlertByID return the alert assiocated to the ID
   168  func (c *Controller) FindAlertByID(gctx *gin.Context) {
   169  	defer types.CatchPanic("synsec/controllersV1/FindAlertByID")
   170  
   171  	alertIDStr := gctx.Param("alert_id")
   172  	alertID, err := strconv.Atoi(alertIDStr)
   173  	if err != nil {
   174  		gctx.JSON(http.StatusBadRequest, gin.H{"message": "alert_id must be valid integer"})
   175  		return
   176  	}
   177  	result, err := c.DBClient.GetAlertByID(alertID)
   178  	if err != nil {
   179  		c.HandleDBErrors(gctx, err)
   180  		return
   181  	}
   182  	data := FormatOneAlert(result)
   183  
   184  	if gctx.Request.Method == "HEAD" {
   185  		gctx.String(http.StatusOK, "")
   186  		return
   187  	}
   188  	gctx.JSON(http.StatusOK, data)
   189  	return
   190  }
   191  
   192  // DeleteAlerts : delete alerts from database based on the specified filter
   193  func (c *Controller) DeleteAlerts(gctx *gin.Context) {
   194  	defer types.CatchPanic("synsec/controllersV1/DeleteAlerts")
   195  
   196  	if gctx.ClientIP() != "127.0.0.1" && gctx.ClientIP() != "::1" {
   197  		gctx.JSON(http.StatusForbidden, gin.H{"message": fmt.Sprintf("access forbidden from this IP (%s)", gctx.ClientIP())})
   198  		return
   199  	}
   200  	var err error
   201  	nbDeleted, err := c.DBClient.DeleteAlertWithFilter(gctx.Request.URL.Query())
   202  	if err != nil {
   203  		c.HandleDBErrors(gctx, err)
   204  	}
   205  
   206  	deleteAlertsResp := models.DeleteAlertsResponse{
   207  		NbDeleted: strconv.Itoa(nbDeleted),
   208  	}
   209  
   210  	gctx.JSON(http.StatusOK, deleteAlertsResp)
   211  	return
   212  }