package middleware import ( "bytes" "io" "strings" "github.com/gin-gonic/gin" "github.com/perfect-panel/server/internal/svc" "github.com/perfect-panel/server/pkg/result" "github.com/perfect-panel/server/pkg/signature" "github.com/perfect-panel/server/pkg/xerr" ) var ( publicSignaturePrefixes = []string{ "/v1/auth", "/v1/auth/oauth", "/v1/common", "/v1/public", } defaultSignatureSkipPrefixes = []string{ "/v1/notify/", "/v1/iap/notifications", "/v1/telegram/webhook", "/v1/subscribe/config", "/v1/common/log/report", } ) func SignatureMiddleware(srvCtx *svc.ServiceContext) func(c *gin.Context) { skipPrefixes := collectSignatureSkipPrefixes(srvCtx) return func(c *gin.Context) { path := c.Request.URL.Path if !isPublicSignaturePath(path) { c.Next() return } for _, prefix := range skipPrefixes { if strings.HasPrefix(path, prefix) { c.Next() return } } if !srvCtx.Config.Signature.EnableSignature { c.Next() return } appId := c.GetHeader("X-App-Id") if appId == "" { c.Next() return } timestamp := c.GetHeader("X-Timestamp") nonce := c.GetHeader("X-Nonce") sig := c.GetHeader("X-Signature") if timestamp == "" || nonce == "" || sig == "" { result.HttpResult(c, nil, xerr.NewErrCode(xerr.SignatureMissing)) c.Abort() return } var bodyBytes []byte if c.Request.Body != nil { bodyBytes, _ = io.ReadAll(c.Request.Body) c.Request.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) } sts := signature.BuildStringToSign( c.Request.Method, path, c.Request.URL.RawQuery, bodyBytes, appId, timestamp, nonce, ) if err := srvCtx.SignatureValidator.Validate(c.Request.Context(), appId, timestamp, nonce, sig, sts); err != nil { result.HttpResult(c, nil, xerr.NewErrCode(mapSignatureErr(err))) c.Abort() return } c.Next() } } func isPublicSignaturePath(path string) bool { for _, prefix := range publicSignaturePrefixes { if strings.HasPrefix(path, prefix) { return true } } return false } func collectSignatureSkipPrefixes(srvCtx *svc.ServiceContext) []string { prefixSet := make(map[string]struct{}, len(defaultSignatureSkipPrefixes)+len(srvCtx.Config.AppSignature.SkipPrefixes)+1) for _, prefix := range defaultSignatureSkipPrefixes { prefixSet[prefix] = struct{}{} } for _, prefix := range srvCtx.Config.AppSignature.SkipPrefixes { if strings.TrimSpace(prefix) == "" { continue } prefixSet[prefix] = struct{}{} } if path := strings.TrimSpace(srvCtx.Config.Subscribe.SubscribePath); path != "" { prefixSet[path] = struct{}{} } prefixes := make([]string, 0, len(prefixSet)) for prefix := range prefixSet { prefixes = append(prefixes, prefix) } return prefixes } func mapSignatureErr(err error) uint32 { switch err { case signature.ErrSignatureMissing: return xerr.SignatureMissing case signature.ErrSignatureExpired: return xerr.SignatureExpired case signature.ErrSignatureReplay: return xerr.SignatureReplay default: return xerr.SignatureInvalid } }