Working webhook

This commit is contained in:
nemunaire 2022-09-05 12:20:33 +02:00
commit 7b45214ca9
6 changed files with 157 additions and 39 deletions

View file

@ -1,6 +1,8 @@
package main
import (
"crypto/rand"
"encoding/base64"
"flag"
"fmt"
"log"
@ -169,34 +171,12 @@ func declareAPIAuthRepositoriesRoutes(router *gin.RouterGroup) {
now := time.Now()
if !work.Shown || work.Corrected || work.StartAvailability.After(now) || work.EndAvailability.Add(time.Hour).Before(now) {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"errmsg": "The submission is closed."})
return
}
if repo.LastCheck != nil && !repo.LastCheck.Before(now.Add(-5*time.Minute)) {
c.AbortWithStatusJSON(http.StatusPaymentRequired, gin.H{"errmsg": "Please wait between two pulls."})
return
}
client := drone.NewClient(droneEndpoint, droneConfig)
result, err := client.BuildCreate("srs", "atsebay.t-worker", "", "master", map[string]string{
"REPO_URL": repo.URI,
"REPO_TAG": work.Tag,
"LOGIN": u.Login,
"DEST": fmt.Sprintf("%d", work.Id),
})
if err != nil {
log.Println("Unable to communicate with Drone:", err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to communication with the extraction service."})
return
}
repo.DroneRef = fmt.Sprintf("%s/%s/%d", "srs", "atsebay.t-worker", result.Number)
repo.LastCheck = &now
repo.Update()
c.JSON(http.StatusOK, repo)
TriggerTagUpdate(c, work, repo, u, nil)
})
repositoriesRoutes.GET("/state", func(c *gin.Context) {
@ -254,20 +234,77 @@ func declareAPIAuthRepositoriesRoutes(router *gin.RouterGroup) {
})
}
type GitLabWebhook struct {
EventName string `json:"event_name"`
ObjectKind string `json:"object_kind"`
Ref string
Repository GitLabRepository
}
func declareCallbacksRoutes(router *gin.RouterGroup) {
router.POST("/callbacks/trigger.json", func(c *gin.Context) {
log.Println("Received trigger")
log.Println("X-Gitlab-Token", c.Request.Header.Get("X-Gitlab-Token"))
// Check event type
if c.Request.Header.Get("X-Gitlab-Event") != "Tag Push Hook" {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "This trigger is limited to Tag Push event. Please edit your trigger."})
return
}
tmp := map[string]interface{}{}
if err := c.ShouldBindJSON(&tmp); err != nil {
hook := GitLabWebhook{}
if err := c.ShouldBindJSON(&hook); err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
return
}
log.Println("Content", tmp)
// Check token
repos, err := getRepositoriesByURI(hook.Repository.URL)
if err != nil {
log.Println("Unable to getRepositoriesByURI:", err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieve repositories."})
return
}
c.JSON(http.StatusOK, true)
var repo *Repository
for _, r := range repos {
log.Println("Received trigger")
if len(r.Secret) == 0 || base64.StdEncoding.EncodeToString(r.Secret) == c.Request.Header.Get("X-Gitlab-Token") {
repo = r
break
}
}
if repo == nil {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "There are no repositories matching this URL and secret. Please check the Gitlab Secret and retry pushing your tag."})
return
}
work, err := getWork(int(repo.IdWork))
if err != nil {
log.Println("Unable to getWork:", err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to find related work."})
return
}
user, err := getUser(int(repo.IdUser))
if err != nil {
log.Println("Unable to getUser:", err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to find related user."})
return
}
tmp := strings.SplitN(hook.Ref, "/", 3)
if len(tmp) != 3 {
TriggerTagUpdate(c, work, repo, user, nil)
return
}
if !strings.HasPrefix(tmp[2], work.Tag) {
c.AbortWithStatusJSON(http.StatusOK, gin.H{"errmsg": fmt.Sprintf("Ignore ref %q has it doesn't start with %s. Check submission instructions if this is not expected.", tmp[2], work.Tag)})
return
}
TriggerTagUpdate(c, work, repo, user, &tmp[2])
})
}
@ -299,24 +336,80 @@ func repositoryHandler(c *gin.Context) {
}
}
func TriggerTagUpdate(c *gin.Context, work *Work, repo *Repository, u *User, tag *string) {
now := time.Now()
if !work.Shown || work.Corrected || work.StartAvailability.After(now) || work.EndAvailability.Add(time.Hour).Before(now) {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"errmsg": "The submission is closed."})
return
}
repo_tag := work.Tag
if tag != nil {
repo_tag = *tag
}
client := drone.NewClient(droneEndpoint, droneConfig)
result, err := client.BuildCreate("srs", "atsebay.t-worker", "", "master", map[string]string{
"REPO_URL": repo.URI,
"REPO_TAG": repo_tag,
"LOGIN": u.Login,
"DEST": fmt.Sprintf("%d", work.Id),
})
if err != nil {
log.Println("Unable to communicate with Drone:", err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to communication with the extraction service."})
return
}
repo.DroneRef = fmt.Sprintf("%s/%s/%d", "srs", "atsebay.t-worker", result.Number)
repo.LastCheck = &now
repo.Update()
repo.Secret = []byte{}
c.JSON(http.StatusOK, repo)
}
type Repository struct {
Id int64 `json:"id"`
IdUser int64 `json:"id_user"`
IdWork int64 `json:"id_work"`
URI string `json:"uri"`
Secret []byte `json:"secret,omitempty"`
LastCheck *time.Time `json:"last_check"`
DroneRef string `json:"drone_ref,omitempty"`
}
func (u *User) GetRepositories() (repositories []*Repository, err error) {
if rows, errr := DBQuery("SELECT id_repository, id_user, id_work, uri, last_check, droneref FROM user_work_repositories WHERE id_user=?", u.Id); errr != nil {
if rows, errr := DBQuery("SELECT id_repository, id_user, id_work, uri, secret, last_check, droneref FROM user_work_repositories WHERE id_user=?", u.Id); errr != nil {
return nil, errr
} else {
defer rows.Close()
for rows.Next() {
var repo Repository
if err = rows.Scan(&repo.Id, &repo.IdUser, &repo.IdWork, &repo.URI, &repo.LastCheck, &repo.DroneRef); err != nil {
if err = rows.Scan(&repo.Id, &repo.IdUser, &repo.IdWork, &repo.URI, &repo.Secret, &repo.LastCheck, &repo.DroneRef); err != nil {
return
}
repositories = append(repositories, &repo)
}
if err = rows.Err(); err != nil {
return
}
return
}
}
func getRepositoriesByURI(uri string) (repositories []*Repository, err error) {
if rows, errr := DBQuery("SELECT id_repository, id_user, id_work, uri, secret, last_check, droneref FROM user_work_repositories WHERE uri=?", uri); errr != nil {
return nil, errr
} else {
defer rows.Close()
for rows.Next() {
var repo Repository
if err = rows.Scan(&repo.Id, &repo.IdUser, &repo.IdWork, &repo.URI, &repo.Secret, &repo.LastCheck, &repo.DroneRef); err != nil {
return
}
repositories = append(repositories, &repo)
@ -331,28 +424,34 @@ func (u *User) GetRepositories() (repositories []*Repository, err error) {
func getRepository(id int) (r *Repository, err error) {
r = new(Repository)
err = DBQueryRow("SELECT id_repository, id_user, id_work, uri, last_check, droneref FROM user_work_repositories WHERE id_repository=?", id).Scan(&r.Id, &r.IdUser, &r.IdWork, &r.URI, &r.LastCheck, &r.DroneRef)
err = DBQueryRow("SELECT id_repository, id_user, id_work, uri, secret, last_check, droneref FROM user_work_repositories WHERE id_repository=?", id).Scan(&r.Id, &r.IdUser, &r.IdWork, &r.URI, &r.Secret, &r.LastCheck, &r.DroneRef)
return
}
func (u *User) getRepository(id int) (r *Repository, err error) {
r = new(Repository)
err = DBQueryRow("SELECT id_repository, id_user, id_work, uri, last_check, droneref FROM user_work_repositories WHERE id_repository=? AND id_user=?", id, u.Id).Scan(&r.Id, &r.IdUser, &r.IdWork, &r.URI, &r.LastCheck, &r.DroneRef)
err = DBQueryRow("SELECT id_repository, id_user, id_work, uri, secret, last_check, droneref FROM user_work_repositories WHERE id_repository=? AND id_user=?", id, u.Id).Scan(&r.Id, &r.IdUser, &r.IdWork, &r.URI, &r.Secret, &r.LastCheck, &r.DroneRef)
return
}
func (u *User) NewRepository(w *Work, uri string) (*Repository, error) {
if res, err := DBExec("INSERT INTO user_work_repositories (id_user, id_work, uri, droneref) VALUES (?, ?, ?, ?)", u.Id, w.Id, uri, ""); err != nil {
secret := make([]byte, 24)
_, err := rand.Read(secret)
if err != nil {
return nil, err
}
if res, err := DBExec("INSERT INTO user_work_repositories (id_user, id_work, uri, secret, droneref) VALUES (?, ?, ?, ?, ?)", u.Id, w.Id, uri, secret, ""); err != nil {
return nil, err
} else if rid, err := res.LastInsertId(); err != nil {
return nil, err
} else {
return &Repository{rid, u.Id, w.Id, uri, nil, ""}, nil
return &Repository{rid, u.Id, w.Id, uri, secret, nil, ""}, nil
}
}
func (r *Repository) Update() (*Repository, error) {
if _, err := DBExec("UPDATE user_work_repositories SET id_user = ?, id_work = ?, uri = ?, last_check = ?, droneref = ? WHERE id_repository = ?", r.IdUser, r.IdWork, r.URI, r.LastCheck, r.DroneRef, r.Id); err != nil {
if _, err := DBExec("UPDATE user_work_repositories SET id_user = ?, id_work = ?, uri = ?, secret = ?, last_check = ?, droneref = ? WHERE id_repository = ?", r.IdUser, r.IdWork, r.URI, r.Secret, r.LastCheck, r.DroneRef, r.Id); err != nil {
return nil, err
} else {
return r, err