diff --git a/api/middleware.go b/api/middleware.go index f9d4028..1956802 100644 --- a/api/middleware.go +++ b/api/middleware.go @@ -1,11 +1,16 @@ package api import ( + "bytes" "code.gitea.io/sdk/gitea" "context" + "crypto/hmac" + "crypto/sha256" + "encoding/hex" "git.lumeweb.com/LumeWeb/gitea-github-proxy/config" "github.com/gorilla/mux" "go.uber.org/zap" + "io" "net/http" "strings" ) @@ -86,6 +91,51 @@ func loggingMiddleware(logger *zap.Logger) mux.MiddlewareFunc { } } +func giteaVerifyWebhookMiddleware(cfg *config.Config, logger *zap.Logger) mux.MiddlewareFunc { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + signature := r.Header.Get("X-Gitea-Signature") + if signature == "" { + http.Error(w, "No signature provided", http.StatusBadRequest) + return + } + + decodedSignature, err := hex.DecodeString(signature) + if err != nil { + http.Error(w, "Error decoding signature", http.StatusBadRequest) + logger.Error("Error decoding signature", zap.Error(err)) + return + } + + payload, err := io.ReadAll(r.Body) + if err != nil { + http.Error(w, "Error reading body", http.StatusBadRequest) + logger.Error("Error reading body", zap.Error(err)) + return + } + defer func(body io.ReadCloser) { + err := body.Close() + if err != nil { + logger.Error("Error closing body", zap.Error(err)) + } + }(r.Body) + + r.Body = io.NopCloser(bytes.NewBuffer(payload)) + + mac := hmac.New(sha256.New, []byte(cfg.GiteaWebHookSecret)) + mac.Write(payload) + expectedMAC := mac.Sum(nil) + + if !hmac.Equal(decodedSignature, expectedMAC) { + http.Error(w, "Invalid signature", http.StatusForbidden) + return + } + + next.ServeHTTP(w, r) + }) + } +} + func addAuthStatusToRequestServ(status bool, r *http.Request, w http.ResponseWriter, next http.Handler) { ctx := context.WithValue(r.Context(), AUTHED_CONTEXT_KEY, status) r = r.WithContext(ctx) diff --git a/config/config.go b/config/config.go index a9af201..3d7b7ae 100644 --- a/config/config.go +++ b/config/config.go @@ -31,13 +31,14 @@ func (p *PrivateKey) UnmarshalText(text []byte) error { } type Config struct { - Host string `mapstructure:"host"` - Port int `mapstructure:"port"` - GiteaUrl string `mapstructure:"gitea_url"` - DbPath string `mapstructure:"db_path"` - Oauth OauthConfig - JwtPrivateKey PrivateKey - Domain string `mapstructure:"domain"` + Host string `mapstructure:"host"` + Port int `mapstructure:"port"` + GiteaUrl string `mapstructure:"gitea_url"` + DbPath string `mapstructure:"db_path"` + Oauth OauthConfig + JwtPrivateKey PrivateKey + Domain string `mapstructure:"domain"` + GiteaWebHookSecret string `mapstructure:"gitea_webhook_secret"` } type OauthConfig struct {