63 lines
1.7 KiB
Go
63 lines
1.7 KiB
Go
package contact
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
const maxChannelRequestBytes = 8 * 1024
|
|
const channelBodyKey = "bell.contact.channel-body"
|
|
const channelBodyErrorKey = "bell.contact.channel-body-error"
|
|
|
|
var redactedChannelBody = []byte(`{"redacted":true}`)
|
|
|
|
// RedactRequestBody must run before GoAdmin's LoggerToFile middleware. The
|
|
// handler restores the original body from Gin context, while sys_opera_log
|
|
// only sees a fixed marker and never the contact address.
|
|
func RedactRequestBody() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
if c.Request.Method != http.MethodPost || !isChannelCreatePath(c.Request.URL.Path) {
|
|
c.Next()
|
|
return
|
|
}
|
|
body, err := io.ReadAll(io.LimitReader(c.Request.Body, maxChannelRequestBytes+1))
|
|
if err != nil {
|
|
c.Set(channelBodyErrorKey, err)
|
|
} else if len(body) > maxChannelRequestBytes {
|
|
c.Set(channelBodyErrorKey, errors.New("request body too large"))
|
|
} else {
|
|
c.Set(channelBodyKey, body)
|
|
}
|
|
_ = c.Request.Body.Close()
|
|
c.Request.Body = io.NopCloser(bytes.NewReader(redactedChannelBody))
|
|
c.Next()
|
|
}
|
|
}
|
|
|
|
func restoreChannelBody(c *gin.Context) error {
|
|
if value, ok := c.Get(channelBodyErrorKey); ok {
|
|
return value.(error)
|
|
}
|
|
value, ok := c.Get(channelBodyKey)
|
|
if !ok {
|
|
return errors.New("channel request body was not captured")
|
|
}
|
|
c.Request.Body = io.NopCloser(bytes.NewReader(value.([]byte)))
|
|
return nil
|
|
}
|
|
|
|
func isChannelCreatePath(path string) bool {
|
|
const prefix = "/api/v1/bell/contacts/"
|
|
const suffix = "/channels"
|
|
if !strings.HasPrefix(path, prefix) || !strings.HasSuffix(path, suffix) {
|
|
return false
|
|
}
|
|
id := strings.TrimSuffix(strings.TrimPrefix(path, prefix), suffix)
|
|
return id != "" && !strings.Contains(id, "/")
|
|
}
|