You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
298 lines
7.9 KiB
Go
298 lines
7.9 KiB
Go
// Copyright 2017 Drone.IO Inc. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package harness
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"encoding/json"
|
|
"io"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"strconv"
|
|
"time"
|
|
|
|
"git.awesome-for.me/liuzhiguo/go-scm/scm"
|
|
"git.awesome-for.me/liuzhiguo/go-scm/scm/driver/internal/hmac"
|
|
)
|
|
|
|
type webhookService struct {
|
|
client *wrapper
|
|
}
|
|
|
|
func (s *webhookService) Parse(req *http.Request, fn scm.SecretFunc) (scm.Webhook, error) {
|
|
data, err := ioutil.ReadAll(
|
|
io.LimitReader(req.Body, 10000000),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var hook scm.Webhook
|
|
switch req.Header.Get("X-Harness-Trigger") {
|
|
// case "create":
|
|
// hook, err = s.parseCreateHook(data)
|
|
// case "delete":
|
|
// hook, err = s.parseDeleteHook(data)
|
|
// case "issues":
|
|
// hook, err = s.parseIssueHook(data)
|
|
case "branch_created":
|
|
hook, err = s.parsePushHook(data)
|
|
case "pullreq_created", "pullreq_reopened", "pullreq_branch_updated":
|
|
hook, err = s.parsePullRequestHook(data)
|
|
case "pullreq_comment_created":
|
|
hook, err = s.parsePullRequestCommentHook(data)
|
|
default:
|
|
return nil, scm.ErrUnknownEvent
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// get the gitea signature key to verify the payload
|
|
// signature. If no key is provided, no validation
|
|
// is performed.
|
|
key, err := fn(hook)
|
|
if err != nil {
|
|
return hook, err
|
|
} else if key == "" {
|
|
return hook, nil
|
|
}
|
|
|
|
secret := req.FormValue("secret")
|
|
signature := req.Header.Get("X-Harness-Signature")
|
|
|
|
// fail if no signature passed
|
|
if signature == "" && secret == "" {
|
|
return hook, scm.ErrSignatureInvalid
|
|
}
|
|
|
|
// test signature if header not set and secret is in payload
|
|
if signature == "" && secret != "" && secret != key {
|
|
return hook, scm.ErrSignatureInvalid
|
|
}
|
|
|
|
// test signature using header
|
|
if signature != "" && !hmac.Validate(sha256.New, data, []byte(key), signature) {
|
|
return hook, scm.ErrSignatureInvalid
|
|
}
|
|
|
|
return hook, nil
|
|
}
|
|
|
|
func (s *webhookService) parsePullRequestHook(data []byte) (scm.Webhook, error) {
|
|
dst := new(pullRequestHook)
|
|
err := json.Unmarshal(data, dst)
|
|
return convertPullRequestHook(dst), err
|
|
}
|
|
|
|
func (s *webhookService) parsePushHook(data []byte) (scm.Webhook, error) {
|
|
dst := new(pushHook)
|
|
err := json.Unmarshal(data, dst)
|
|
return convertPushHook(dst), err
|
|
}
|
|
|
|
func (s *webhookService) parsePullRequestCommentHook(data []byte) (scm.Webhook, error) {
|
|
dst := new(pullRequestCommentHook)
|
|
err := json.Unmarshal(data, dst)
|
|
return convertPullRequestCommentHook(dst), err
|
|
}
|
|
|
|
// native data structures
|
|
type (
|
|
repo struct {
|
|
ID int `json:"id"`
|
|
Path string `json:"path"`
|
|
UID string `json:"uid"`
|
|
DefaultBranch string `json:"default_branch"`
|
|
GitURL string `json:"git_url"`
|
|
}
|
|
principal struct {
|
|
ID int `json:"id"`
|
|
UID string `json:"uid"`
|
|
DisplayName string `json:"display_name"`
|
|
Email string `json:"email"`
|
|
Type string `json:"type"`
|
|
Created int64 `json:"created"`
|
|
Updated int64 `json:"updated"`
|
|
}
|
|
pullReq struct {
|
|
Number int `json:"number"`
|
|
State string `json:"state"`
|
|
IsDraft bool `json:"is_draft"`
|
|
Title string `json:"title"`
|
|
SourceRepoID int `json:"source_repo_id"`
|
|
SourceBranch string `json:"source_branch"`
|
|
TargetRepoID int `json:"target_repo_id"`
|
|
TargetBranch string `json:"target_branch"`
|
|
MergeStrategy interface{} `json:"merge_strategy"`
|
|
Author principal `json:"author"`
|
|
}
|
|
targetRef struct {
|
|
Name string `json:"name"`
|
|
Repo struct {
|
|
ID int `json:"id"`
|
|
Path string `json:"path"`
|
|
UID string `json:"uid"`
|
|
DefaultBranch string `json:"default_branch"`
|
|
GitURL string `json:"git_url"`
|
|
} `json:"repo"`
|
|
}
|
|
ref struct {
|
|
Name string `json:"name"`
|
|
Repo struct {
|
|
ID int `json:"id"`
|
|
Path string `json:"path"`
|
|
UID string `json:"uid"`
|
|
DefaultBranch string `json:"default_branch"`
|
|
GitURL string `json:"git_url"`
|
|
} `json:"repo"`
|
|
}
|
|
hookCommit struct {
|
|
Sha string `json:"sha"`
|
|
Message string `json:"message"`
|
|
Author struct {
|
|
Identity struct {
|
|
Name string `json:"name"`
|
|
Email string `json:"email"`
|
|
} `json:"identity"`
|
|
When string `json:"when"`
|
|
} `json:"author"`
|
|
Committer struct {
|
|
Identity struct {
|
|
Name string `json:"name"`
|
|
Email string `json:"email"`
|
|
} `json:"identity"`
|
|
When string `json:"when"`
|
|
} `json:"committer"`
|
|
}
|
|
comment struct {
|
|
ID int `json:"id"`
|
|
Text string `json:"text"`
|
|
}
|
|
// harness pull request webhook payload
|
|
pullRequestHook struct {
|
|
Trigger string `json:"trigger"`
|
|
Repo repo `json:"repo"`
|
|
Principal principal `json:"principal"`
|
|
PullReq pullReq `json:"pull_req"`
|
|
TargetRef targetRef `json:"target_ref"`
|
|
Ref ref `json:"ref"`
|
|
Sha string `json:"sha"`
|
|
Commit hookCommit `json:"commit"`
|
|
}
|
|
// harness push webhook payload
|
|
pushHook struct {
|
|
Trigger string `json:"trigger"`
|
|
Repo repo `json:"repo"`
|
|
Principal principal `json:"principal"`
|
|
Ref ref `json:"ref"`
|
|
Commit hookCommit `json:"commit"`
|
|
Sha string `json:"sha"`
|
|
OldSha string `json:"old_sha"`
|
|
Forced bool `json:"forced"`
|
|
}
|
|
// harness pull request comment webhook payload
|
|
pullRequestCommentHook struct {
|
|
Trigger string `json:"trigger"`
|
|
Repo repo `json:"repo"`
|
|
Principal principal `json:"principal"`
|
|
PullReq pullReq `json:"pull_req"`
|
|
TargetRef targetRef `json:"target_ref"`
|
|
Ref ref `json:"ref"`
|
|
Sha string `json:"sha"`
|
|
Commit hookCommit `json:"commit"`
|
|
Comment comment `json:"comment"`
|
|
}
|
|
)
|
|
|
|
// native data structure conversion
|
|
func convertPullRequestHook(src *pullRequestHook) *scm.PullRequestHook {
|
|
return &scm.PullRequestHook{
|
|
Action: convertAction(src.Trigger),
|
|
PullRequest: convertPullReq(src.PullReq, src.Ref, src.Commit),
|
|
Repo: convertRepo(src.Repo),
|
|
Sender: convertUser(src.Principal),
|
|
}
|
|
}
|
|
|
|
func convertPushHook(src *pushHook) *scm.PushHook {
|
|
return &scm.PushHook{
|
|
Ref: src.Sha,
|
|
Before: src.OldSha,
|
|
After: src.Sha,
|
|
Repo: convertRepo(src.Repo),
|
|
Commit: scm.Commit{
|
|
Sha: src.Commit.Sha,
|
|
Message: src.Commit.Message,
|
|
Author: scm.Signature{
|
|
Name: src.Commit.Author.Identity.Name,
|
|
Email: src.Commit.Author.Identity.Email,
|
|
},
|
|
},
|
|
Sender: convertUser(src.Principal),
|
|
}
|
|
}
|
|
|
|
func convertPullRequestCommentHook(src *pullRequestCommentHook) *scm.PullRequestCommentHook {
|
|
return &scm.PullRequestCommentHook{
|
|
PullRequest: convertPullReq(src.PullReq, src.Ref, src.Commit),
|
|
Repo: convertRepo(src.Repo),
|
|
Comment: scm.Comment{
|
|
Body: src.Comment.Text,
|
|
ID: src.Comment.ID,
|
|
},
|
|
Sender: convertUser(src.Principal),
|
|
}
|
|
}
|
|
|
|
func convertAction(src string) (action scm.Action) {
|
|
switch src {
|
|
case "pullreq_created":
|
|
return scm.ActionCreate
|
|
case "pullreq_branch_updated":
|
|
return scm.ActionUpdate
|
|
case "pullreq_reopened":
|
|
return scm.ActionReopen
|
|
default:
|
|
return
|
|
}
|
|
}
|
|
|
|
func convertPullReq(pr pullReq, ref ref, commit hookCommit) scm.PullRequest {
|
|
return scm.PullRequest{
|
|
Number: pr.Number,
|
|
Title: pr.Title,
|
|
Closed: pr.State != "open",
|
|
Source: pr.SourceBranch,
|
|
Target: pr.TargetBranch,
|
|
Fork: "fork",
|
|
Link: ref.Repo.GitURL,
|
|
Sha: commit.Sha,
|
|
Ref: ref.Name,
|
|
Author: convertUser(pr.Author),
|
|
}
|
|
}
|
|
|
|
func convertRepo(repo repo) scm.Repository {
|
|
return scm.Repository{
|
|
ID: strconv.Itoa(repo.ID),
|
|
Name: repo.UID,
|
|
Branch: repo.DefaultBranch,
|
|
Link: repo.GitURL,
|
|
Clone: repo.GitURL,
|
|
}
|
|
}
|
|
|
|
func convertUser(principal principal) scm.User {
|
|
return scm.User{
|
|
Name: principal.DisplayName,
|
|
ID: principal.UID,
|
|
Login: principal.UID,
|
|
Email: principal.Email,
|
|
Created: time.Unix(0, principal.Created*int64(time.Millisecond)),
|
|
Updated: time.Unix(0, principal.Updated*int64(time.Millisecond)),
|
|
}
|
|
}
|