From ac60d1daef2e2ad845b242bab4e47989f8d8b0bb Mon Sep 17 00:00:00 2001 From: Nathan Coad Date: Mon, 8 Jan 2024 15:35:13 +1100 Subject: [PATCH] updates --- controllers/retrieve_secrets.go | 120 +++++++++++++++++++------------- controllers/store_secrets.go | 5 +- models/secret.go | 43 ++++++------ models/user.go | 3 +- 4 files changed, 97 insertions(+), 74 deletions(-) diff --git a/controllers/retrieve_secrets.go b/controllers/retrieve_secrets.go index 05167ef..20f04e5 100644 --- a/controllers/retrieve_secrets.go +++ b/controllers/retrieve_secrets.go @@ -29,8 +29,8 @@ type ListSecret struct { func RetrieveSecret(c *gin.Context) { var input RetrieveInput - var results []models.Secret - var userIsAdmin bool = false + //var results []models.Secret + //var userIsAdmin bool = false // Validate the input matches our struct if err := c.ShouldBindJSON(&input); err != nil { @@ -55,55 +55,67 @@ func RetrieveSecret(c *gin.Context) { s.DeviceCategory = input.DeviceCategory s.UserName = input.UserName - user_id, err := token.ExtractTokenID(c) - if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "error determining user"}) - return - } + retrieveSpecifiedSecret(&s, c) - // Work out which safe to query for this user if the safe was not specified - safeList, err := models.UserGetSafesAllowed(int(user_id)) - - // If there was only one result then just use that - if len(safeList) == 0 { - // check if the user is an admin, if not then they seem to have access to zero safes - if !models.UserCheckIfAdmin(int(user_id)) { - c.JSON(http.StatusBadRequest, gin.H{"error": "user has no access to any secrets"}) + /* + user_id, err := token.ExtractTokenID(c) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "error determining user"}) return } - // Don't apply a role filter if user has admin role - results, err = models.GetSecrets(&s, userIsAdmin) - } else if len(safeList) == 1 { - s.SafeId = safeList[0].SafeId - userIsAdmin = safeList[0].AdminUser || safeList[0].AdminGroup - // Don't apply a role filter if user has admin role - results, err = models.GetSecrets(&s, userIsAdmin) - } else { - // TODO - this is tricky. How to query multiple safes? - var safeIds []int - for _, safe := range safeList { - safeIds = append(safeIds, safe.SafeId) + // Work out which safe to query for this user if the safe was not specified + safeList, err := models.UserGetSafesAllowed(int(user_id)) + + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "error determining user safes"}) + return } - results, err = models.SecretsGetMultipleSafes(&s, false, safeIds) - } + // If there was only one result then just use that + if len(safeList) == 0 { + // check if the user is an admin, if not then they seem to have access to zero safes + if !models.UserCheckIfAdmin(int(user_id)) { + c.JSON(http.StatusBadRequest, gin.H{"error": "user has no access to any secrets"}) + return + } else { + // Don't apply a role filter if user has admin role + results, err = models.SecretsGetMultipleSafes(&s, true, []int{}) + } - if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) - return - } + } else if len(safeList) == 1 { + s.SafeId = safeList[0].SafeId + userIsAdmin = safeList[0].AdminUser || safeList[0].AdminGroup + // Don't apply a role filter if user has admin role + //results, err = models.GetSecrets(&s, userIsAdmin) + results, err = models.SecretsGetMultipleSafes(&s, userIsAdmin, []int{s.SafeId}) + } else { + // Create a list of all the safes this user can access + var safeIds []int + for _, safe := range safeList { + safeIds = append(safeIds, safe.SafeId) + } - if len(results) == 1 { - // output results as json - c.JSON(http.StatusOK, gin.H{"message": "success", "data": results}) - } else if len(results) > 1 { - c.JSON(http.StatusBadRequest, gin.H{"error": "found multiple matching secrets, use retrieveMultiple instead"}) - return - } else { - c.JSON(http.StatusBadRequest, gin.H{"error": "found no matching secrets"}) - return - } + results, err = models.SecretsGetMultipleSafes(&s, false, safeIds) + } + + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + + if len(results) == 1 { + // output results as json + c.JSON(http.StatusOK, gin.H{"message": "success", "data": results}) + } else if len(results) > 1 { + c.JSON(http.StatusBadRequest, gin.H{"error": "found multiple matching secrets, use retrieveMultiple instead"}) + return + } else { + c.JSON(http.StatusBadRequest, gin.H{"error": "found no matching secrets"}) + return + } + */ } func RetrieveSecretByDevicename(c *gin.Context) { @@ -163,23 +175,30 @@ func retrieveSpecifiedSecret(s *models.Secret, c *gin.Context) { // Work out which safe to query for this user if the safe was not specified safeList, err := models.UserGetSafesAllowed(int(user_id)) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "error determining user safes"}) + return + } + // If there was only one result then just use that if len(safeList) == 0 { // check if the user is an admin, if not then they seem to have access to zero safes if !models.UserCheckIfAdmin(int(user_id)) { c.JSON(http.StatusBadRequest, gin.H{"error": "user has no access to any secrets"}) return + } else { + // Don't apply a role filter if user has admin role + results, err = models.SecretsGetMultipleSafes(s, true, []int{}) } - // Don't apply a role filter if user has admin role - results, err = models.GetSecrets(s, userIsAdmin) + } else if len(safeList) == 1 { s.SafeId = safeList[0].SafeId userIsAdmin = safeList[0].AdminUser || safeList[0].AdminGroup // Don't apply a role filter if user has admin role - results, err = models.GetSecrets(s, userIsAdmin) + //results, err = models.GetSecrets(&s, userIsAdmin) + results, err = models.SecretsGetMultipleSafes(s, userIsAdmin, []int{s.SafeId}) } else { - // TODO - this is tricky. How to query multiple safes? - + // Create a list of all the safes this user can access var safeIds []int for _, safe := range safeList { safeIds = append(safeIds, safe.SafeId) @@ -188,6 +207,11 @@ func retrieveSpecifiedSecret(s *models.Secret, c *gin.Context) { results, err = models.SecretsGetMultipleSafes(s, false, safeIds) } + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + if len(results) == 1 { // output results as json c.JSON(http.StatusOK, gin.H{"message": "success", "data": results}) diff --git a/controllers/store_secrets.go b/controllers/store_secrets.go index b2f58db..72448b5 100644 --- a/controllers/store_secrets.go +++ b/controllers/store_secrets.go @@ -70,13 +70,14 @@ func StoreSecret(c *gin.Context) { return } + // TODO - replace this with a call to SecretsGetMultipleSafes + // If this secret already exists in the database then generate an error checkExists, err := models.GetSecrets(&s, false) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } - if len(checkExists) > 0 { log.Printf("StoreSecret not storing secret with '%d' already matching secrets.\n", len(checkExists)) c.JSON(http.StatusBadRequest, gin.H{"error": "StoreSecret attempting to store secret already defined. Use update API call instead"}) @@ -156,6 +157,8 @@ func UpdateSecret(c *gin.Context) { s.SafeId = safeId + // TODO - replace this with a call to SecretsGetMultipleSafes + // Confirm that the secret already exists checkExists, err := models.GetSecrets(&s, false) if err != nil { diff --git a/models/secret.go b/models/secret.go index e6e0111..9e7eb62 100644 --- a/models/secret.go +++ b/models/secret.go @@ -46,7 +46,7 @@ func (s *Secret) SaveSecret() (*Secret, error) { return s, nil } -// TODO - use this function when user has access to multiple safes +// SecretsGetMultipleSafes queries the specified safes for matching secrets func SecretsGetMultipleSafes(s *Secret, adminRole bool, safeIds []int) ([]Secret, error) { var err error var secretResults []Secret @@ -62,22 +62,29 @@ func SecretsGetMultipleSafes(s *Secret, adminRole bool, safeIds []int) ([]Secret rows, err := db.Queryx(query, args) */ - // Generate placeholders for the IN clause to match multiple SafeId values - placeholders := make([]string, len(safeIds)) - for i := range safeIds { - placeholders[i] = "?" - } - placeholderStr := strings.Join(placeholders, ",") - - query := fmt.Sprintf("SELECT * FROM secrets WHERE SafeId IN (%s) ", placeholderStr) args := []interface{}{} + var query string + if adminRole { + // No need to limit query to any safe + query = "SELECT * FROM secrets WHERE 1=1 " + } else { + // Generate placeholders for the IN clause to match multiple SafeId values + placeholders := make([]string, len(safeIds)) + for i := range safeIds { + placeholders[i] = "?" + } + placeholderStr := strings.Join(placeholders, ",") - // Add the Safe Ids - for _, g := range safeIds { - args = append(args, g) + // Create query with the necessary placeholders + query = fmt.Sprintf("SELECT * FROM secrets WHERE SafeId IN (%s) ", placeholderStr) + + // Add the Safe Ids to the arguments list + for _, g := range safeIds { + args = append(args, g) + } } - // Add any other parameters + // Add any other arguments to the query if they were specified if s.DeviceName != "" { query += " AND DeviceName LIKE ? " args = append(args, s.DeviceName) @@ -93,16 +100,6 @@ func SecretsGetMultipleSafes(s *Secret, adminRole bool, safeIds []int) ([]Secret args = append(args, s.UserName) } - /* - // Construct the query - query := fmt.Sprintf("SELECT * FROM users WHERE username = ? AND group IN (%s)", placeholderStr) - args := make([]interface{}, 0, len(groups)+1) - args = append(args, username) - for _, g := range safeIds { - args = append(args, g) - } - */ - // Execute the query rows, err := db.Queryx(query, args...) diff --git a/models/user.go b/models/user.go index 465b611..55af3ac 100644 --- a/models/user.go +++ b/models/user.go @@ -364,8 +364,7 @@ func UserGetSafesAllowed(userId int) ([]UserSafe, error) { INNER JOIN groups ON users.GroupId = groups.GroupId INNER JOIN permissions ON groups.GroupId = permissions.GroupId INNER JOIN safes on permissions.SafeId = safes.SafeId - WHERE users.UserId=?" - `, userId) + WHERE users.UserId=?`, userId) if err != nil { log.Printf("UserGetSafesAllowed error executing sql record : '%s'\n", err) return results, err