From 99dafce303c24388196e0a5ef0c4ff98b7def4b3 Mon Sep 17 00:00:00 2001 From: "Urko." Date: Mon, 29 Apr 2024 22:24:00 +0200 Subject: [PATCH] feat: handle gitea webhook --- .gitignore | 3 ++ app.example.yml | 2 ++ go.mod | 5 +++ go.sum | 4 +++ kit/config/config.go | 28 ++++++++++++++++ main.go | 80 ++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 122 insertions(+) create mode 100755 .gitignore create mode 100644 app.example.yml create mode 100644 go.mod create mode 100644 go.sum create mode 100755 kit/config/config.go create mode 100644 main.go diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..cc17444 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +configs/* +coverage/* +bin \ No newline at end of file diff --git a/app.example.yml b/app.example.yml new file mode 100644 index 0000000..9647cef --- /dev/null +++ b/app.example.yml @@ -0,0 +1,2 @@ +secret: "" +port: 8824 \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..80355f8 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module gitea.urkob.com/urko/gitea-webhook-listener + +go 1.22.0 + +require gopkg.in/yaml.v2 v2.4.0 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..dd0bc19 --- /dev/null +++ b/go.sum @@ -0,0 +1,4 @@ +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/kit/config/config.go b/kit/config/config.go new file mode 100755 index 0000000..32d93bb --- /dev/null +++ b/kit/config/config.go @@ -0,0 +1,28 @@ +package config + +import ( + "os" + + "gopkg.in/yaml.v2" +) + +type Config struct { + Secret string `yaml:"secret"` + Port int `yaml:"port"` + BinaryPath string `yaml:"binary_path"` + ScriptPath string `yaml:"script_path"` +} + +func LoadConfig(path string) (*Config, error) { + data, err := os.ReadFile(path) + if err != nil { + return nil, err + } + + var config Config + if err := yaml.Unmarshal(data, &config); err != nil { + return nil, err + } + + return &config, nil +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..e418d49 --- /dev/null +++ b/main.go @@ -0,0 +1,80 @@ +package main + +import ( + "crypto/hmac" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "fmt" + "io" + "net/http" + "os" + "os/exec" + + "gitea.urkob.com/urko/gitea-webhook-listener/kit/config" +) + +func main() { + cfg, err := config.LoadConfig(".configs/app.yml") + if err != nil { + panic(err) + } + http.HandleFunc("/payload", handlePayload(cfg.Secret, cfg.BinaryPath, cfg.ScriptPath)) + http.ListenAndServe(fmt.Sprintf(":%d", cfg.Port), nil) +} + +func handlePayload(secret, binaryPath, scriptPath string) func(w http.ResponseWriter, r *http.Request) { + return (func(w http.ResponseWriter, r *http.Request) { + + // Read the request body + body, err := io.ReadAll(r.Body) + if err != nil { + http.Error(w, "Failed to read request body", http.StatusBadRequest) + return + } + defer r.Body.Close() + + // Verify the signature + if !verifySignature(body, r.Header.Get("X-Hub-Signature-256"), []byte(secret)) { + http.Error(w, "Signatures didn't match", http.StatusUnauthorized) + return + } + + // Parse the JSON payload + var payload interface{} + err = json.Unmarshal(body, &payload) + if err != nil { + http.Error(w, "Failed to parse JSON payload", http.StatusBadRequest) + return + } + + // TODO: Do something with the payload + fmt.Fprintf(w, "I got some JSON: %v", payload) + + if err := execute(binaryPath, scriptPath); err != nil { + panic(err) + } + }) +} + +func execute(binaryPath, scriptPath string) error { + cmd := exec.Command(binaryPath, scriptPath) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + if err := cmd.Run(); err != nil { + return fmt.Errorf("cmd.Run %w", err) + } + + return nil +} + +func verifySignature(payload []byte, signature string, secret []byte) bool { + // Compute the expected signature + mac := hmac.New(sha256.New, secret) + mac.Write(payload) + expectedSignature := hex.EncodeToString(mac.Sum(nil)) + + // Compare the expected signature with the actual signature + return hmac.Equal([]byte(signature), []byte("sha256="+expectedSignature)) +}