encrypted_params.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. package middleware
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "io"
  6. "mime/multipart"
  7. "net/http"
  8. "net/url"
  9. "strings"
  10. "github.com/0xJacky/Nginx-UI/internal/crypto"
  11. "github.com/gin-gonic/gin"
  12. "github.com/uozi-tech/cosy"
  13. "github.com/uozi-tech/cosy/logger"
  14. )
  15. var (
  16. e = cosy.NewErrorScope("middleware")
  17. ErrInvalidRequestFormat = e.New(40000, "invalid request format")
  18. ErrDecryptionFailed = e.New(40001, "decryption failed")
  19. ErrFormParseFailed = e.New(40002, "form parse failed")
  20. )
  21. func EncryptedParams() gin.HandlerFunc {
  22. return func(c *gin.Context) {
  23. // read the encrypted payload
  24. var encryptedReq struct {
  25. EncryptedParams string `json:"encrypted_params"`
  26. }
  27. if err := c.ShouldBindJSON(&encryptedReq); err != nil {
  28. c.AbortWithStatusJSON(http.StatusBadRequest, ErrInvalidRequestFormat)
  29. return
  30. }
  31. // decrypt the parameters
  32. decryptedData, err := crypto.Decrypt(encryptedReq.EncryptedParams)
  33. if err != nil {
  34. c.AbortWithStatusJSON(http.StatusBadRequest, ErrDecryptionFailed)
  35. return
  36. }
  37. // replace request body with decrypted data
  38. newBody, _ := json.Marshal(decryptedData)
  39. c.Request.Body = io.NopCloser(bytes.NewReader(newBody))
  40. c.Request.ContentLength = int64(len(newBody))
  41. c.Next()
  42. }
  43. }
  44. // EncryptedForm handles multipart/form-data with encrypted fields while preserving file uploads
  45. func EncryptedForm() gin.HandlerFunc {
  46. return func(c *gin.Context) {
  47. // Only process if the content type is multipart/form-data
  48. if !strings.Contains(c.GetHeader("Content-Type"), "multipart/form-data") {
  49. c.Next()
  50. return
  51. }
  52. // Parse the multipart form
  53. if err := c.Request.ParseMultipartForm(512 << 20); err != nil { // 512MB max memory
  54. c.AbortWithStatusJSON(http.StatusBadRequest, ErrFormParseFailed)
  55. return
  56. }
  57. // Check if encrypted_params field exists
  58. encryptedParams := c.Request.FormValue("encrypted_params")
  59. if encryptedParams == "" {
  60. // No encryption, continue normally
  61. c.Next()
  62. return
  63. }
  64. // Decrypt the parameters
  65. params, err := crypto.Decrypt(encryptedParams)
  66. if err != nil {
  67. c.AbortWithStatusJSON(http.StatusBadRequest, ErrDecryptionFailed)
  68. return
  69. }
  70. // Create a new multipart form with the decrypted data
  71. newForm := &multipart.Form{
  72. Value: make(map[string][]string),
  73. File: c.Request.MultipartForm.File, // Keep original file uploads
  74. }
  75. // Add decrypted values to the new form
  76. for key, val := range params {
  77. strVal, ok := val.(string)
  78. if ok {
  79. newForm.Value[key] = []string{strVal}
  80. } else {
  81. // Handle other types if necessary
  82. jsonVal, _ := json.Marshal(val)
  83. newForm.Value[key] = []string{string(jsonVal)}
  84. }
  85. }
  86. // Also copy original non-encrypted form values (except encrypted_params)
  87. for key, vals := range c.Request.MultipartForm.Value {
  88. if key != "encrypted_params" && newForm.Value[key] == nil {
  89. newForm.Value[key] = vals
  90. }
  91. }
  92. logger.Debug("newForm values", newForm.Value)
  93. logger.Debug("newForm files", newForm.File)
  94. // Replace the original form with our modified one
  95. c.Request.MultipartForm = newForm
  96. // Remove the encrypted_params field from the form
  97. delete(c.Request.MultipartForm.Value, "encrypted_params")
  98. // Reset ContentLength as form structure has changed
  99. c.Request.ContentLength = -1
  100. // Sync the form values to the request PostForm to ensure Gin can access them
  101. if c.Request.PostForm == nil {
  102. c.Request.PostForm = make(url.Values)
  103. }
  104. // Copy all values from MultipartForm to PostForm
  105. for k, v := range newForm.Value {
  106. c.Request.PostForm[k] = v
  107. }
  108. logger.Debug("PostForm after sync", c.Request.PostForm)
  109. c.Next()
  110. }
  111. }