123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128 |
- package middleware
- import (
- "bytes"
- "encoding/json"
- "io"
- "mime/multipart"
- "net/http"
- "net/url"
- "strings"
- "github.com/0xJacky/Nginx-UI/internal/crypto"
- "github.com/gin-gonic/gin"
- "github.com/uozi-tech/cosy"
- )
- var (
- e = cosy.NewErrorScope("middleware")
- ErrInvalidRequestFormat = e.New(40000, "invalid request format")
- ErrDecryptionFailed = e.New(40001, "decryption failed")
- ErrFormParseFailed = e.New(40002, "form parse failed")
- )
- func EncryptedParams() gin.HandlerFunc {
- return func(c *gin.Context) {
- // read the encrypted payload
- var encryptedReq struct {
- EncryptedParams string `json:"encrypted_params"`
- }
- if err := c.ShouldBindJSON(&encryptedReq); err != nil {
- c.AbortWithStatusJSON(http.StatusBadRequest, ErrInvalidRequestFormat)
- return
- }
- // decrypt the parameters
- decryptedData, err := crypto.Decrypt(encryptedReq.EncryptedParams)
- if err != nil {
- c.AbortWithStatusJSON(http.StatusBadRequest, ErrDecryptionFailed)
- return
- }
- // replace request body with decrypted data
- newBody, _ := json.Marshal(decryptedData)
- c.Request.Body = io.NopCloser(bytes.NewReader(newBody))
- c.Request.ContentLength = int64(len(newBody))
- c.Next()
- }
- }
- // EncryptedForm handles multipart/form-data with encrypted fields while preserving file uploads
- func EncryptedForm() gin.HandlerFunc {
- return func(c *gin.Context) {
- // Only process if the content type is multipart/form-data
- if !strings.Contains(c.GetHeader("Content-Type"), "multipart/form-data") {
- c.Next()
- return
- }
- // Parse the multipart form
- if err := c.Request.ParseMultipartForm(512 << 20); err != nil { // 512MB max memory
- c.AbortWithStatusJSON(http.StatusBadRequest, ErrFormParseFailed)
- return
- }
- // Check if encrypted_params field exists
- encryptedParams := c.Request.FormValue("encrypted_params")
- if encryptedParams == "" {
- // No encryption, continue normally
- c.Next()
- return
- }
- // Decrypt the parameters
- params, err := crypto.Decrypt(encryptedParams)
- if err != nil {
- c.AbortWithStatusJSON(http.StatusBadRequest, ErrDecryptionFailed)
- return
- }
- // Create a new multipart form with the decrypted data
- newForm := &multipart.Form{
- Value: make(map[string][]string),
- File: c.Request.MultipartForm.File, // Keep original file uploads
- }
- // Add decrypted values to the new form
- for key, val := range params {
- strVal, ok := val.(string)
- if ok {
- newForm.Value[key] = []string{strVal}
- } else {
- // Handle other types if necessary
- jsonVal, _ := json.Marshal(val)
- newForm.Value[key] = []string{string(jsonVal)}
- }
- }
- // Also copy original non-encrypted form values (except encrypted_params)
- for key, vals := range c.Request.MultipartForm.Value {
- if key != "encrypted_params" && newForm.Value[key] == nil {
- newForm.Value[key] = vals
- }
- }
- // Replace the original form with our modified one
- c.Request.MultipartForm = newForm
- // Remove the encrypted_params field from the form
- delete(c.Request.MultipartForm.Value, "encrypted_params")
- // Reset ContentLength as form structure has changed
- c.Request.ContentLength = -1
- // Sync the form values to the request PostForm to ensure Gin can access them
- if c.Request.PostForm == nil {
- c.Request.PostForm = make(url.Values)
- }
- // Copy all values from MultipartForm to PostForm
- for k, v := range newForm.Value {
- c.Request.PostForm[k] = v
- }
- c.Next()
- }
- }
|