aboutsummaryrefslogtreecommitdiff
path: root/application_router.go
diff options
context:
space:
mode:
Diffstat (limited to 'application_router.go')
-rw-r--r--application_router.go145
1 files changed, 132 insertions, 13 deletions
diff --git a/application_router.go b/application_router.go
index 501956b..4048dc5 100644
--- a/application_router.go
+++ b/application_router.go
@@ -1,3 +1,20 @@
+/*
+ * This file is part of caronte (https://github.com/eciavatta/caronte).
+ * Copyright (c) 2020 Emiliano Ciavatta.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
package main
import (
@@ -13,7 +30,8 @@ import (
"time"
)
-func CreateApplicationRouter(applicationContext *ApplicationContext) *gin.Engine {
+func CreateApplicationRouter(applicationContext *ApplicationContext,
+ notificationController *NotificationController, resourcesController *ResourcesController) *gin.Engine {
router := gin.New()
router.Use(gin.Logger())
router.Use(gin.Recovery())
@@ -21,7 +39,7 @@ func CreateApplicationRouter(applicationContext *ApplicationContext) *gin.Engine
router.Use(static.Serve("/", static.LocalFile("./frontend/build", true)))
- for _, path := range []string{"/connections/:id", "/pcaps", "/rules", "/services", "/config"} {
+ for _, path := range []string{"/connections/:id", "/pcaps", "/rules", "/services", "/config", "/searches"} {
router.GET(path, func(c *gin.Context) {
c.File("./frontend/build/index.html")
})
@@ -47,6 +65,13 @@ func CreateApplicationRouter(applicationContext *ApplicationContext) *gin.Engine
applicationContext.SetAccounts(settings.Accounts)
c.JSON(http.StatusAccepted, gin.H{})
+ notificationController.Notify("setup", gin.H{})
+ })
+
+ router.GET("/ws", func(c *gin.Context) {
+ if err := notificationController.NotificationHandler(c.Writer, c.Request); err != nil {
+ serverError(c, err)
+ }
})
api := router.Group("/api")
@@ -68,7 +93,9 @@ func CreateApplicationRouter(applicationContext *ApplicationContext) *gin.Engine
if id, err := applicationContext.RulesManager.AddRule(c, rule); err != nil {
unprocessableEntity(c, err)
} else {
- success(c, UnorderedDocument{"id": id})
+ response := UnorderedDocument{"id": id}
+ success(c, response)
+ notificationController.Notify("rules.new", response)
}
})
@@ -107,6 +134,7 @@ func CreateApplicationRouter(applicationContext *ApplicationContext) *gin.Engine
notFound(c, UnorderedDocument{"id": id})
} else {
success(c, rule)
+ notificationController.Notify("rules.edit", rule)
}
})
@@ -119,14 +147,16 @@ func CreateApplicationRouter(applicationContext *ApplicationContext) *gin.Engine
flushAllValue, isPresent := c.GetPostForm("flush_all")
flushAll := isPresent && strings.ToLower(flushAllValue) == "true"
fileName := fmt.Sprintf("%v-%s", time.Now().UnixNano(), fileHeader.Filename)
- if err := c.SaveUploadedFile(fileHeader, ProcessingPcapsBasePath + fileName); err != nil {
+ if err := c.SaveUploadedFile(fileHeader, ProcessingPcapsBasePath+fileName); err != nil {
log.WithError(err).Panic("failed to save uploaded file")
}
if sessionID, err := applicationContext.PcapImporter.ImportPcap(fileName, flushAll); err != nil {
unprocessableEntity(c, err)
} else {
- c.JSON(http.StatusAccepted, gin.H{"session": sessionID})
+ response := gin.H{"session": sessionID}
+ c.JSON(http.StatusAccepted, response)
+ notificationController.Notify("pcap.upload", response)
}
})
@@ -147,7 +177,7 @@ func CreateApplicationRouter(applicationContext *ApplicationContext) *gin.Engine
}
fileName := fmt.Sprintf("%v-%s", time.Now().UnixNano(), filepath.Base(request.File))
- if err := CopyFile(ProcessingPcapsBasePath + fileName, request.File); err != nil {
+ if err := CopyFile(ProcessingPcapsBasePath+fileName, request.File); err != nil {
log.WithError(err).Panic("failed to copy pcap file")
}
if sessionID, err := applicationContext.PcapImporter.ImportPcap(fileName, request.FlushAll); err != nil {
@@ -158,7 +188,9 @@ func CreateApplicationRouter(applicationContext *ApplicationContext) *gin.Engine
}
unprocessableEntity(c, err)
} else {
- c.JSON(http.StatusAccepted, gin.H{"session": sessionID})
+ response := gin.H{"session": sessionID}
+ c.JSON(http.StatusAccepted, response)
+ notificationController.Notify("pcap.file", response)
}
})
@@ -179,9 +211,9 @@ func CreateApplicationRouter(applicationContext *ApplicationContext) *gin.Engine
sessionID := c.Param("id")
if _, isPresent := applicationContext.PcapImporter.GetSession(sessionID); isPresent {
if FileExists(PcapsBasePath + sessionID + ".pcap") {
- c.FileAttachment(PcapsBasePath + sessionID + ".pcap", sessionID[:16] + ".pcap")
+ c.FileAttachment(PcapsBasePath+sessionID+".pcap", sessionID[:16]+".pcap")
} else if FileExists(PcapsBasePath + sessionID + ".pcapng") {
- c.FileAttachment(PcapsBasePath + sessionID + ".pcapng", sessionID[:16] + ".pcapng")
+ c.FileAttachment(PcapsBasePath+sessionID+".pcapng", sessionID[:16]+".pcapng")
} else {
log.WithField("sessionID", sessionID).Panic("pcap file not exists")
}
@@ -195,6 +227,7 @@ func CreateApplicationRouter(applicationContext *ApplicationContext) *gin.Engine
session := gin.H{"session": sessionID}
if cancelled := applicationContext.PcapImporter.CancelSession(sessionID); cancelled {
c.JSON(http.StatusAccepted, session)
+ notificationController.Notify("sessions.delete", session)
} else {
notFound(c, session)
}
@@ -253,24 +286,89 @@ func CreateApplicationRouter(applicationContext *ApplicationContext) *gin.Engine
}
if result {
- c.Status(http.StatusAccepted)
+ response := gin.H{"connection_id": c.Param("id"), "action": c.Param("action")}
+ success(c, response)
+ notificationController.Notify("connections.action", response)
} else {
notFound(c, gin.H{"connection": id})
}
})
+ api.GET("/searches", func(c *gin.Context) {
+ success(c, applicationContext.SearchController.GetPerformedSearches())
+ })
+
+ api.POST("/searches/perform", func(c *gin.Context) {
+ var options SearchOptions
+
+ if err := c.ShouldBindJSON(&options); err != nil {
+ badRequest(c, err)
+ return
+ }
+
+ // stupid checks because validator library is a shit
+ var badContentError error
+ if options.TextSearch.isZero() == options.RegexSearch.isZero() {
+ badContentError = errors.New("specify either 'text_search' or 'regex_search'")
+ }
+ if !options.TextSearch.isZero() {
+ if (options.TextSearch.Terms == nil) == (options.TextSearch.ExactPhrase == "") {
+ badContentError = errors.New("specify either 'terms' or 'exact_phrase'")
+ }
+ if (options.TextSearch.Terms == nil) && (options.TextSearch.ExcludedTerms != nil) {
+ badContentError = errors.New("'excluded_terms' must be specified only with 'terms'")
+ }
+ }
+ if !options.RegexSearch.isZero() {
+ if (options.RegexSearch.Pattern == "") == (options.RegexSearch.NotPattern == "") {
+ badContentError = errors.New("specify either 'pattern' or 'not_pattern'")
+ }
+ }
+
+ if badContentError != nil {
+ badRequest(c, badContentError)
+ return
+ }
+
+ success(c, applicationContext.SearchController.PerformSearch(c, options))
+ })
+
api.GET("/streams/:id", func(c *gin.Context) {
id, err := RowIDFromHex(c.Param("id"))
if err != nil {
badRequest(c, err)
return
}
- var format QueryFormat
+ var format GetMessageFormat
if err := c.ShouldBindQuery(&format); err != nil {
badRequest(c, err)
return
}
- success(c, applicationContext.ConnectionStreamsController.GetConnectionPayload(c, id, format))
+
+ if messages, found := applicationContext.ConnectionStreamsController.GetConnectionMessages(c, id, format); !found {
+ notFound(c, gin.H{"connection": id})
+ } else {
+ success(c, messages)
+ }
+ })
+
+ api.GET("/streams/:id/download", func(c *gin.Context) {
+ id, err := RowIDFromHex(c.Param("id"))
+ if err != nil {
+ badRequest(c, err)
+ return
+ }
+ var format DownloadMessageFormat
+ if err := c.ShouldBindQuery(&format); err != nil {
+ badRequest(c, err)
+ return
+ }
+
+ if blob, found := applicationContext.ConnectionStreamsController.DownloadConnectionMessages(c, id, format); !found {
+ notFound(c, gin.H{"connection": id})
+ } else {
+ c.String(http.StatusOK, blob)
+ }
})
api.GET("/services", func(c *gin.Context) {
@@ -285,13 +383,30 @@ func CreateApplicationRouter(applicationContext *ApplicationContext) *gin.Engine
}
if err := applicationContext.ServicesController.SetService(c, service); err == nil {
success(c, service)
+ notificationController.Notify("services.edit", service)
} else {
unprocessableEntity(c, err)
}
})
- }
+ api.GET("/statistics", func(c *gin.Context) {
+ var filter StatisticsFilter
+ if err := c.ShouldBindQuery(&filter); err != nil {
+ badRequest(c, err)
+ return
+ }
+ success(c, applicationContext.StatisticsController.GetStatistics(c, filter))
+ })
+
+ api.GET("/resources/system", func(c *gin.Context) {
+ success(c, resourcesController.GetSystemStats(c))
+ })
+
+ api.GET("/resources/process", func(c *gin.Context) {
+ success(c, resourcesController.GetProcessStats(c))
+ })
+ }
return router
}
@@ -352,3 +467,7 @@ func unprocessableEntity(c *gin.Context, err error) {
func notFound(c *gin.Context, obj interface{}) {
c.JSON(http.StatusNotFound, obj)
}
+
+func serverError(c *gin.Context, err error) {
+ c.JSON(http.StatusInternalServerError, UnorderedDocument{"result": "error", "error": err.Error()})
+}