diff --git a/src/backend/controllers/webhook.go b/src/backend/controllers/webhook.go index adb09b5..5fd0635 100644 --- a/src/backend/controllers/webhook.go +++ b/src/backend/controllers/webhook.go @@ -2,87 +2,58 @@ package controllers import ( "bytes" - "crypto/hmac" "io/ioutil" + "net/http" hmacService "git.sebclem.fr/sebclem/renovate-manager/services/hmac" + giteaService "git.sebclem.fr/sebclem/renovate-manager/services/webhook/gitea" "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" ) -type WebhookController struct{} +type WebhookController struct { + WebhookSecret string + RenovateUsername string +} -func (ctl WebhookController) HandleGiteaWebhook(c *gin.Context) { +func (ctrl WebhookController) HandleGiteaWebhook(c *gin.Context) { r := c.Request signature := r.Header.Get("X-Gitea-Signature") body, err := ioutil.ReadAll(r.Body) if err != nil { - c.AbortWithStatus(500) + c.AbortWithStatus(http.StatusInternalServerError) return } - expectedSignature := hmacService.CalculateHMAC("test", body) - - if signature == "" || !hmac.Equal([]byte(signature), []byte(expectedSignature)) { - logrus.Error("Signature missmatch, Abort !") - c.AbortWithStatus(401) + if !hmacService.CompareSignature(body, signature, ctrl.WebhookSecret) { + c.AbortWithStatus(http.StatusUnauthorized) return } - // Reset the request body so it can be Bind to json + // Reset the request body so it can be read again by BindJSON c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(body)) switch r.Header.Get("X-Gitea-Event") { case "push": - logrus.Info("Received PUSH webhook") - case "create": - logrus.Info("Received CREATE webhook") - case "delete": - logrus.Info("Received DELETE webhook") - case "issues": - logrus.Info("Received ISSUES webhook") + logrus.Debug("Received PUSH webhook") case "issue_comment": - logrus.Info("Received ISSUE_COMMENT webhook") - case "pull_request": - logrus.Info("Received PULL_REQUEST webhook") - var data PullRequestWebhook + logrus.Debug("Received ISSUE_COMMENT webhook") + var data giteaService.IssueComentWebhook if err := c.BindJSON(&data); err == nil { - logrus.Info("All good") + giteaService.HandleGiteaIssueComment(data, ctrl.RenovateUsername) + c.String(201, "") + } else { + logrus.Error("Fail to parse json") + } + case "pull_request": + logrus.Debug("Received PULL_REQUEST webhook") + var data giteaService.PullRequestWebhook + if err := c.BindJSON(&data); err == nil { + giteaService.HandleGiteaPullRequest(data, ctrl.RenovateUsername) c.String(201, "") } else { logrus.Error("Fail to parse json") } - default: - logrus.Warn("Received UNKNOWN webhook") } - // ctx.String(201, "") -} - -type PullRequestWebhook struct { - Action string `json:"action"` - Number int `json:"number"` - PullRequest PullRequest `json:"pull_request"` - Repository Repository `json:"repository"` - Sender User `json:"sender"` -} - -type PullRequest struct { - Id int `json:"id"` - Url string `json:"url"` - Number uint64 `json:"number"` - User User `json:"user"` -} - -type User struct { - Id int `json:"id"` - Login string `json:"login"` - Username string `json:"username"` -} - -type Repository struct { - Id int `json:"id"` - Owner User `json:"owner"` - Name string `json:"name"` - FullName string `json:"full_name"` } diff --git a/src/backend/routes/webhook.go b/src/backend/routes/webhook.go index 5a6c981..88d9135 100644 --- a/src/backend/routes/webhook.go +++ b/src/backend/routes/webhook.go @@ -6,6 +6,6 @@ import ( ) func LoadWebhook(g *gin.RouterGroup) { - controller := new(controllers.WebhookController) + controller := controllers.WebhookController{WebhookSecret: "test", RenovateUsername: "renovate-bot"} g.POST("gitea", controller.HandleGiteaWebhook) } diff --git a/src/backend/services/hmac/hmac.go b/src/backend/services/hmac/hmac.go index 91a5c66..26043a9 100644 --- a/src/backend/services/hmac/hmac.go +++ b/src/backend/services/hmac/hmac.go @@ -4,6 +4,8 @@ import ( "crypto/hmac" "crypto/sha256" "encoding/hex" + + "github.com/sirupsen/logrus" ) func CalculateHMAC(secret string, message []byte) string { @@ -12,3 +14,13 @@ func CalculateHMAC(secret string, message []byte) string { h.Write(message) return hex.EncodeToString(h.Sum(nil)) } + +func CompareSignature(body []byte, signature string, secret string) bool { + expectedSignature := CalculateHMAC(secret, body) + + if signature == "" || !hmac.Equal([]byte(signature), []byte(expectedSignature)) { + logrus.Error("Signature missmatch, Abort !") + return false + } + return true +} diff --git a/src/backend/services/webhook/gitea/gitea.go b/src/backend/services/webhook/gitea/gitea.go new file mode 100644 index 0000000..d3c2b01 --- /dev/null +++ b/src/backend/services/webhook/gitea/gitea.go @@ -0,0 +1,82 @@ +package gitea + +import ( + "strings" + + "github.com/sirupsen/logrus" +) + +func HandleGiteaPullRequest(data PullRequestWebhook, renovateUsername string) { + if !isFromRenovate(data.Sender, renovateUsername) { + if isEditOnRenovatePR(data, renovateUsername) { + logrus.Info("Edit on renovate PR, launch renovate") + logrus.Info("Repo: ", data.Repository.FullName, " PR: ", data.Number) + } + } +} + +func HandleGiteaIssueComment(data IssueComentWebhook, renovateUsername string) { + if !isFromRenovate(data.Sender, renovateUsername) { + if strings.Contains(data.Comment.Body, "@"+renovateUsername) { + logrus.Info("Mention in comment, launch renovate") + logrus.Info("Repo: ", data.Repository.FullName) + } + } +} + +func isFromRenovate(sender User, renovateUsername string) bool { + return sender.Username == renovateUsername +} + +func isRenovatePR(data PullRequest, renovateUsername string) bool { + return data.User.Username == renovateUsername +} + +func isEditOnRenovatePR(data PullRequestWebhook, renovateUsername string) bool { + if data.Action == "edited" { + return isRenovatePR(data.PullRequest, renovateUsername) + } + return false +} + +type PullRequestWebhook struct { + Action string `json:"action"` + Number int `json:"number"` + PullRequest PullRequest `json:"pull_request"` + Repository Repository `json:"repository"` + Sender User `json:"sender"` +} + +type IssueComentWebhook struct { + Action string `json:"action"` + Comment Comment `json:"comment"` + Repository Repository `json:"repository"` + Sender User `json:"sender"` + IsPull bool `json:"is_pull"` +} + +type PullRequest struct { + Id int `json:"id"` + Url string `json:"url"` + Number uint64 `json:"number"` + User User `json:"user"` +} + +type Comment struct { + Id int `json:"id"` + User User `json:"user"` + Body string `json:"body"` +} + +type User struct { + Id int `json:"id"` + Login string `json:"login"` + Username string `json:"username"` +} + +type Repository struct { + Id int `json:"id"` + Owner User `json:"owner"` + Name string `json:"name"` + FullName string `json:"full_name"` +}