My Very Own CI-server
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

391 lines
14 KiB

package web
import (
"io"
"io/ioutil"
"bytes"
"strings"
"strconv"
//"regexp"
neturl "net/url"
"net/http"
"net/http/httputil"
"crypto/hmac"
"crypto/sha256"
"crypto/sha1"
"encoding/hex"
. "mvoCI/core"
"mvoCI/core"
"mvoCI/build"
. "mvoCI/hook"
//"golang.org/x/crypto/bcrypt"
//"github.com/jinzhu/gorm"
"github.com/labstack/echo"
//"github.com/labstack/echo/middleware"
)
type hmacFunc func ( []byte, []byte ) string
func SecretCheck ( data io.Reader, secret string, theirs string, method string ) bool {
var fn hmacFunc
if method == "sha1" {
fn = hmacSha1
} else if ( method == "sha256" ) {
fn = hmacSha256
} else {
Console.Log ("BUG: method not found")
return false
}
var buff []byte
buff, err := ioutil.ReadAll ( data )
if err == nil {
sha := fn ( buff, []byte(secret) )
var theirs string = theirs
if sha == theirs {
return true
}
}
return false
}
func hmacSha1 ( data []byte, secret []byte ) string {
h := hmac.New(sha1.New, []byte(secret))
h.Write([]byte(data))
return hex.EncodeToString(h.Sum(nil))
}
func hmacSha256 ( data []byte, secret []byte ) string {
h := hmac.New(sha256.New, []byte(secret))
h.Write([]byte(data))
return hex.EncodeToString(h.Sum(nil))
}
func branchFromRef ( ref string ) string {
var tmp []string
tmp = strings.Split ( ref, "/" )
return tmp[ len(tmp)-1 ]
}
func webhook ( ctx echo.Context ) error {
var api string;
var err error
var tmp []byte;
var HookLog WebHookLog = WebHookLog{}
// skip this costly operation if not necessary
if s.cfg.WebHookLog {
tmp, _ = httputil.DumpRequest ( ctx.Request(), true )
}
api = ctx.Param("api")
body := ctx.Request().Body
var bodyBuff bytes.Buffer
rd := io.TeeReader ( body, &bodyBuff )
var bID uint
// body for an error message
var ResponseBody string = "Did not work :("
// status for the error case
var ResponseStatus int = 500
var event string
HookLog.Request = string(tmp)
HookLog.API = api
HookLog.Status = "Failure";
var author string
var message string
var url string
switch api {
case "gogs":
event = ctx.Request().Header["X-Github-Event"][0]
if event == "push" {
var pl GogsPayload
err = GenericJSONDecode ( rd, &pl )
if err != nil {
break
}
r := core.Repository{};
s.db.Where("clone_url LIKE ?", pl.Repository.Clone_Url ).First ( &r )
if r.ID > 0 && r.WebHookEnable == true{
HookLog.Repository = r;
sig := ctx.Request().Header["X-Gogs-Signature"][0]
if SecretCheck ( &bodyBuff, r.Secret, sig, "sha256" ) {
author = pl.Pusher.Full_Name;
if len(author) < 1 {
author = pl.Pusher.Login
}
message = pl.Commits[0].Message
url = pl.Commits[0].Url
bID = build.BuildSpecific2 ( s.db, r, branchFromRef ( pl.Ref ), pl.After, author, url, message, event )
} else {
ResponseBody = "Signature check failed"
}
} else {
bID = 0
ResponseBody = "Repo not found"
ResponseStatus = 404
}
} else {
ResponseBody = "Listening to 'push', got '" + event +"'"
}
case "gitea":
event = ctx.Request().Header["X-Gitea-Event"][0]
if event == "push" || event == "release" {
var pl GiteaPayload
err = GenericJSONDecode ( rd, &pl )
if err != nil {
break
}
//bID = build.BuildHookedFromSecret ( s.db, pl.Secret, branchFromRef ( pl.Ref ), pl.After )
r := core.Repository{};
s.db.Where("clone_url LIKE ?", pl.Repository.Clone_Url ).First ( &r )
if r.ID > 0 && r.WebHookEnable == true {
HookLog.Repository = r;
sig := ctx.Request().Header["X-Gitea-Signature"][0]
if SecretCheck ( &bodyBuff, r.Secret, sig, "sha256" ) {
if event == "push" {
author = pl.Pusher.Full_Name;
if len(author) < 1 {
author = pl.Pusher.Login
}
if len(pl.Commits) > 0 {
message = pl.Commits[0].Message
url = pl.Commits[0].Url
bID = build.BuildSpecific2 ( s.db, r, branchFromRef ( pl.Ref ), pl.After, author, url, message, event )
}
} else if event == "release" {
author = pl.Release.Author.Full_Name;
if len(author) < 1 {
author = pl.Release.Author.Login
}
message = "Release: " + pl.Release.TagName + " - " + pl.Release.Name
url = pl.Release.HtmlUrl
apiUrl := pl.Release.Url
bID = build.BuildRelease ( s.db, r, pl.Release.TargetCommitish, pl.Release.TagName, author, url, message, event, api, apiUrl );
}
} else {
ResponseBody = "Signature check failed"
}
} else {
bID = 0
ResponseBody = "Repo not found"
ResponseStatus = 404
}
} else {
ResponseBody = "Listening to 'push', got '" + event +"'"
}
case "gitbucket":
event = ctx.Request().Header["X-Github-Event"][0]
if event == "push" {
var escaped string
var e []byte
e, err = ioutil.ReadAll ( rd );
if err != nil {
ResponseBody = "Invalid gitbucket request";
ResponseStatus = 500;
}
escaped = string(e);
escaped, err = neturl.QueryUnescape ( escaped[8:] );
if err != nil {
ResponseBody = "Invalid gitbucket request";
ResponseStatus = 500;
}
rd = strings.NewReader ( escaped );
var pl GitBucketPayload
err = GenericJSONDecode ( rd, &pl )
if err != nil {
break
}
r := core.Repository{};
s.db.Where("clone_url LIKE ? OR clone_url LIKE ?", pl.Repository.Clone_Url, pl.Repository.Http_Url ).First ( &r )
if r.ID > 0 && r.WebHookEnable == true {
HookLog.Repository = r;
sig := strings.Split( ctx.Request().Header["X-Hub-Signature"][0] , "=")
if SecretCheck ( &bodyBuff, r.Secret, sig[1], sig[0] ) {
if len(pl.Commits) > 0 {
message = pl.Commits[0].Message;
author = pl.Commits[0].Author.Name;
url = pl.Commits[0].Html_Url;
}
bID = build.BuildSpecific2 ( s.db, r, branchFromRef ( pl.Ref ), pl.After, author, url, message, event )
} else {
ResponseBody = "Signature check failed"
}
} else {
bID = 0
ResponseBody = "Repo not found"
ResponseStatus = 404
}
} else {
ResponseBody = "Listening to 'push', got '" + event +"'"
}
case "bitbucket":
event = ctx.Request().Header["X-Event-Key"][0]
if event == "diagnostics:ping" {
ResponseBody = "Ok";
ResponseStatus = 200;
} else if event == "repo:refs_changed" {
var pl BitBucketPayload
err = GenericJSONDecode ( rd, &pl )
if err != nil {
break
}
var links []string
for _, l := range pl.Repository.Links.Clone {
links = append ( links, l.Href );
}
r := core.Repository{};
s.db.Where("clone_url IN (?)", links ).First ( &r )
if r.ID > 0 && r.WebHookEnable == true{
HookLog.Repository = r;
sig := strings.Split( ctx.Request().Header["X-Hub-Signature"][0] , "=")
if SecretCheck ( &bodyBuff, r.Secret, sig[1], sig[0] ) {
author = pl.Actor.DisplayName
if author == "" {
author = pl.Actor.Name
}
var toHash, ref string
if len ( pl.Changes ) > 0 {
ref = pl.Changes[0].RefId
toHash = pl.Changes[0].ToHash
}
bID = build.BuildSpecific2 ( s.db, r, branchFromRef ( ref ), toHash, author, url, message, event )
} else {
ResponseBody = "Signature check failed"
}
} else {
bID = 0
ResponseBody = "Repo not found"
ResponseStatus = 404
}
} else {
ResponseBody = "Listening to 'repo:refs_changed', got '" + event +"'"
}
case "github":
event = ctx.Request().Header["X-Github-Event"][0]
if event == "push" {
var pl GithubPayload
err = GenericJSONDecode ( rd, &pl )
if err != nil {
break
}
r := core.Repository{};
s.db.Where("clone_url LIKE ? OR clone_url LIKE ? OR clone_url LIKE ?", pl.Repository.Clone_Url, pl.Repository.Git_Url, pl.Repository.Ssh_Url ).First ( &r )
if r.ID > 0 && r.WebHookEnable == true {
HookLog.Repository = r;
sig := strings.Split( ctx.Request().Header["X-Hub-Signature"][0] , "=")
if SecretCheck ( &bodyBuff, r.Secret, sig[1], sig[0] ) {
bID = build.BuildSpecific2 ( s.db, r, branchFromRef ( pl.Ref ), pl.After, author, url, message, event )
} else {
ResponseBody = "Signature check failed"
}
} else {
bID = 0
ResponseBody = "Repo not found"
ResponseStatus = 404
}
} else {
ResponseBody = "Listening to 'push', got '" + event +"'"
}
case "local":
var pl LocalPayload
err = GenericJSONDecode ( rd, &pl )
if err != nil {
break
}
if pl.Action != "build" {
ResponseBody = "Invalid Action"
ResponseStatus = 403
bID=0
} else {
r := core.Repository{}
s.db.Where("id = ?", pl.RepositoryID).First(&r)
if r.ID != pl.RepositoryID || r.Secret != pl.Secret || r.Name != pl.RepositoryName || r.LocalBuildEnable != true {
ResponseBody = "Repository was not found"
ResponseStatus = 405
bID = 0
} else {
HookLog.Repository = r;
bID = build.BuildNow ( r, s.db )
}
}
case "gitlab":
event = ctx.Request().Header["X-Gitlab-Event"][0]
if event == "Push Hook" {
var pl GitlabPayload
err = GenericJSONDecode ( rd, &pl )
if err != nil {
break
}
//bID = build.BuildHookedFromSecret ( s.db, pl.Secret, branchFromRef ( pl.Ref ), pl.After )
r := core.Repository{};
s.db.Where("clone_url LIKE ? OR clone_url LIKE ? or clone_url LIKE ? or clone_url LIKE ?", pl.Repository.GitSshUrl, pl.Repository.GitHttpUrl, pl.Project.GitHttpUrl, pl.Project.GitSshUrl ).First ( &r )
if r.ID > 0 {
HookLog.Repository = r;
sig := ctx.Request().Header["X-Gitlab-Token"][0]
if sig == r.Secret && r.WebHookEnable == true {
author = pl.UserName;
if len(author) < 1 {
author = pl.UserUsername
}
message = pl.Message
if len(pl.Commits) > 0 {
message = pl.Commits[0].Message
url = pl.Commits[0].Url
}
bID = build.BuildSpecific2 ( s.db, r, branchFromRef ( pl.Ref ), pl.After, author, url, message, event )
} else {
ResponseBody = "Signature check failed"
}
} else {
bID = 0
ResponseBody = "Repo not found"
ResponseStatus = 404
}
} else {
ResponseBody = "Listening to 'Push Hook', got '" + event +"'"
}
default:
ResponseBody = "API not implemented"
ResponseStatus = 404
// woopsie!
}
if bID > 0 {
HookLog.Status = "Success";
HookLog.BuildID = bID;
ResponseStatus = http.StatusOK;
ResponseBody = "Enqueued, build #" + strconv.Itoa ( int(bID) )
}
HookLog.ResponseBody = ResponseBody;
HookLog.ResponseStatus = ResponseStatus
if s.cfg.WebHookLog {
s.db.Save ( &HookLog );
}
return ctx.String ( ResponseStatus, ResponseBody )
}