diff --git a/backend/api/controllers/v1/backends/lookup.go b/backend/api/controllers/v1/backends/lookup.go new file mode 100644 index 0000000..189184b --- /dev/null +++ b/backend/api/controllers/v1/backends/lookup.go @@ -0,0 +1,148 @@ +package backends + +import ( + "encoding/base64" + "fmt" + "net/http" + "strings" + + "git.terah.dev/imterah/hermes/api/dbcore" + "git.terah.dev/imterah/hermes/api/jwtcore" + "git.terah.dev/imterah/hermes/api/permissions" + "github.com/charmbracelet/log" + "github.com/gin-gonic/gin" + "github.com/go-playground/validator/v10" +) + +type BackendLookupRequest struct { + Token string `validate:"required"` + BackendID *uint `json:"id"` + Name *string + Description *string + Backend *string +} + +type SanitizedBackend struct { + Name string `json:"name"` + BackendID uint `json:"id"` + OwnerID uint `json:"ownerID"` + Description *string `json:"description"` + Backend string `json:"backend"` + BackendParameters *string `json:"connectionDetails"` +} + +type LookupResponse struct { + Success bool `json:"success"` + Data []*SanitizedBackend `json:"data"` +} + +func LookupBackend(c *gin.Context) { + var req BackendLookupRequest + + if err := c.BindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "error": fmt.Sprintf("Failed to parse body: %s", err.Error()), + }) + + return + } + + if err := validator.New().Struct(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "error": fmt.Sprintf("Failed to validate body: %s", err.Error()), + }) + + return + } + + user, err := jwtcore.GetUserFromJWT(req.Token) + + if err != nil { + if err.Error() == "token is expired" || err.Error() == "user does not exist" { + c.JSON(http.StatusForbidden, gin.H{ + "error": err.Error(), + }) + + return + } else { + log.Warnf("Failed to get user from the provided JWT token: %s", err.Error()) + + c.JSON(http.StatusInternalServerError, gin.H{ + "error": "Failed to parse token", + }) + + return + } + } + + if !permissions.UserHasPermission(user, "backends.visible") { + c.JSON(http.StatusForbidden, gin.H{ + "error": "Missing permissions", + }) + + return + } + + backends := []dbcore.Backend{} + queryString := []string{} + queryParameters := []interface{}{} + + if req.BackendID != nil { + queryString = append(queryString, "id = ?") + queryParameters = append(queryParameters, req.BackendID) + } + + if req.Name != nil { + queryString = append(queryString, "name = ?") + queryParameters = append(queryParameters, req.Name) + } + + if req.Description != nil { + queryString = append(queryString, "description = ?") + queryParameters = append(queryParameters, req.Description) + } + + if req.Backend != nil { + queryString = append(queryString, "isbot = ?") + queryParameters = append(queryParameters, req.Backend) + } + + if err := dbcore.DB.Where(strings.Join(queryString, " AND "), queryParameters...).Find(&backends).Error; err != nil { + log.Warnf("Failed to get backends: %s", err.Error()) + + c.JSON(http.StatusInternalServerError, gin.H{ + "error": "Failed to get backends", + }) + + return + } + + sanitizedBackends := make([]*SanitizedBackend, len(backends)) + hasSecretVisibility := permissions.UserHasPermission(user, "backends.secretVis") + + for backendIndex, backend := range backends { + sanitizedBackends[backendIndex] = &SanitizedBackend{ + BackendID: backend.ID, + OwnerID: backend.UserID, + Name: backend.Name, + Description: backend.Description, + Backend: backend.Backend, + } + + if backend.UserID == user.ID || hasSecretVisibility { + backendParametersBytes, err := base64.StdEncoding.DecodeString(backend.BackendParameters) + + if err != nil { + log.Warnf("Failed to decode base64 backend parameters: %s", err.Error()) + } + + backendParameters := string(backendParametersBytes) + sanitizedBackends[backendIndex].BackendParameters = &backendParameters + } + } + + c.JSON(http.StatusOK, &LookupResponse{ + Success: true, + Data: sanitizedBackends, + }) +} diff --git a/backend/api/main.go b/backend/api/main.go index 5904305..885e55c 100644 --- a/backend/api/main.go +++ b/backend/api/main.go @@ -193,6 +193,7 @@ func entrypoint(cCtx *cli.Context) error { engine.POST("/api/v1/backends/create", backends.CreateBackend) engine.POST("/api/v1/backends/remove", backends.RemoveBackend) + engine.POST("/api/v1/backends/lookup", backends.LookupBackend) log.Infof("Listening on '%s'", listeningAddress) err = engine.Run(listeningAddress) diff --git a/routes/Hermes API/Backend/Lookup.bru b/routes/Hermes API/Backend/Lookup.bru index 47ef0a1..7365c64 100644 --- a/routes/Hermes API/Backend/Lookup.bru +++ b/routes/Hermes API/Backend/Lookup.bru @@ -5,13 +5,13 @@ meta { } post { - url: http://127.0.0.1:3000/api/v1/backends/lookup + url: http://127.0.0.1:8000/api/v1/backends/lookup body: json auth: none } body:json { { - "token": "7d69814cdada551dd22521ad97b23b22a106278826a2b4e87dd76246594b56f973894e8265437a5d520ed7258d7c856d0d294e89b1de1a98db7fa4a" + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiMSJdLCJleHAiOjE3MzUwNzY0MTEsIm5iZiI6MTczNDk5MDAxMSwiaWF0IjoxNzM0OTkwMDExfQ.N9TLraX4peHt7FKv8tPcHuEzL0K7T2IBEw3piQS_4OY" } }