From 931d345ec425a84a1193f4e97d73756184644144 Mon Sep 17 00:00:00 2001 From: Nathan Coad Date: Mon, 3 Apr 2023 09:17:42 +1000 Subject: [PATCH] add update endpoint --- controllers/retrieve_secrets.go | 3 +- controllers/store_secrets.go | 67 ++++++++++++++++++++++++++++++++- main.go | 1 + models/secret.go | 31 ++++++++++++++- models/setup.go | 2 +- 5 files changed, 98 insertions(+), 6 deletions(-) diff --git a/controllers/retrieve_secrets.go b/controllers/retrieve_secrets.go index 506c51c..c0c22b3 100644 --- a/controllers/retrieve_secrets.go +++ b/controllers/retrieve_secrets.go @@ -3,7 +3,6 @@ package controllers import ( "ccsecrets/models" "ccsecrets/utils/token" - "errors" "fmt" "net/http" @@ -53,7 +52,7 @@ func RetrieveSecret(c *gin.Context) { } if len(results) > 1 { - c.JSON(http.StatusBadRequest, gin.H{"error": errors.New("found multiple matching secrets, use retrieveMultiple instead")}) + c.JSON(http.StatusBadRequest, gin.H{"error": "found multiple matching secrets, use retrieveMultiple instead"}) return } diff --git a/controllers/store_secrets.go b/controllers/store_secrets.go index 23f983f..dc608eb 100644 --- a/controllers/store_secrets.go +++ b/controllers/store_secrets.go @@ -2,6 +2,7 @@ package controllers import ( "ccsecrets/models" + "errors" "fmt" "net/http" @@ -12,7 +13,7 @@ import ( type StoreInput struct { RoleId int `json:"roleId"` DeviceName string `json:"deviceName"` - DeviceCategory string `json:"devicCategory"` + DeviceCategory string `json:"deviceCategory"` UserName string `json:"userName" binding:"required"` SecretValue string `json:"secretValue" binding:"required"` } @@ -70,3 +71,67 @@ func StoreSecret(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"message": "secret stored successfully"}) } + +func UpdateSecret(c *gin.Context) { + var err error + var input StoreInput + + if err := c.ShouldBindJSON(&input); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + fmt.Printf("UpdateSecret received JSON input '%v'\n", input) + + // TODO - verify that the user role is not readonly + + // Populate fields + s := models.Secret{} + s.UserName = input.UserName + s.DeviceName = input.DeviceName + s.DeviceCategory = input.DeviceCategory + + // Default role ID is 1 if not defined + if input.RoleId != 0 { + s.RoleId = input.RoleId + } else { + s.RoleId = 1 + } + + // Confirm that the secret already exists + checkExists, err := models.GetSecrets(&s) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + if len(checkExists) == 0 { + err = errors.New("UpdateSecret could not find existing secret to update") + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } else if len(checkExists) == 1 { + // Set the secret id with the one retrieved from the database + s.SecretId = checkExists[0].SecretId + + // Encrypt secret + s.Secret = input.SecretValue + _, err = s.EncryptSecret() + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"Error encrypting secret": err.Error()}) + return + } + + _, err = s.UpdateSecret() + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"Error saving secret": err.Error()}) + return + } + + c.JSON(http.StatusOK, gin.H{"message": "secret updated successfully"}) + } else { + err = errors.New("UpdateSecret found multiple secrets matching input data, be more specific") + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + +} diff --git a/main.go b/main.go index 5d230e0..63741d5 100644 --- a/main.go +++ b/main.go @@ -120,6 +120,7 @@ func main() { protected.GET("/retrieve", controllers.RetrieveSecret) protected.GET("/retrieveMultiple", controllers.RetrieveMultpleSecrets) protected.POST("/store", controllers.StoreSecret) + protected.POST("/update", controllers.UpdateSecret) // Initializing the server in a goroutine so that // it won't block the graceful shutdown handling below diff --git a/models/secret.go b/models/secret.go index 4132bfb..c268915 100644 --- a/models/secret.go +++ b/models/secret.go @@ -34,7 +34,7 @@ func (s *Secret) SaveSecret() (*Secret, error) { if err != nil { fmt.Printf("StoreSecret error executing sql record : '%s'\n", err) - return &Secret{}, err + return s, err } else { affected, _ := result.RowsAffected() id, _ := result.LastInsertId() @@ -54,7 +54,9 @@ func GetSecrets(s *Secret) ([]Secret, error) { // Determine whether to query for a specific device or a category of devices // Prefer querying device name than category - if s.DeviceName != "" { + if s.DeviceName != "" && s.DeviceCategory != "" { + rows, err = db.Queryx("SELECT * FROM secrets WHERE DeviceName LIKE ? AND DeviceCategory LIKE ? AND RoleId = ?", s.DeviceName, s.DeviceCategory, s.RoleId) + } else if s.DeviceName != "" { rows, err = db.Queryx("SELECT * FROM secrets WHERE DeviceName LIKE ? AND RoleId = ?", s.DeviceName, s.RoleId) } else if s.DeviceCategory != "" { rows, err = db.Queryx("SELECT * FROM secrets WHERE DeviceCategory LIKE ? AND RoleId = ?", s.DeviceCategory, s.RoleId) @@ -92,6 +94,31 @@ func GetSecrets(s *Secret) ([]Secret, error) { return secretResults, nil } +func (s *Secret) UpdateSecret() (*Secret, error) { + + var err error + + fmt.Printf("UpdateSecret storing values '%v'\n", s) + + if s.SecretId == 0 { + err = errors.New("UpdateSecret unable to locate secret with empty secretId field") + fmt.Printf("UpdateSecret error in pre-check : '%s'\n", err) + return s, err + } + + result, err := db.NamedExec((`UPDATE secrets SET DeviceName = :DeviceName, DeviceCategory = :DeviceCategory, UserName = :UserName, Secret = :Secret WHERE SecretId = :SecretId`), s) + if err != nil { + fmt.Printf("UpdateSecret error executing sql record : '%s'\n", err) + return &Secret{}, err + } else { + affected, _ := result.RowsAffected() + id, _ := result.LastInsertId() + fmt.Printf("UpdateSecret insert returned result id '%d' affecting %d row(s).\n", id, affected) + } + + return s, nil +} + func (s *Secret) EncryptSecret() (*Secret, error) { keyString := os.Getenv("SECRETS_KEY") diff --git a/models/setup.go b/models/setup.go index aa12eb9..0a85195 100644 --- a/models/setup.go +++ b/models/setup.go @@ -150,7 +150,7 @@ func CreateTables() { fmt.Printf("Error checking schema table : '%s'", err) os.Exit(1) } - schemaCheck, _ := CheckColumnExists("schema", "version") + schemaCheck, _ := CheckColumnExists("schema", "Version") if !schemaCheck { if _, err = db.Exec("INSERT INTO schema VALUES(1);"); err != nil { fmt.Printf("Error adding initial scehama version : '%s'", err)