Skip to content

Commit

Permalink
Merge pull request #5 from soh335/feature/x-hub-signature
Browse files Browse the repository at this point in the history
support X-Hub-Signature
  • Loading branch information
Konboi committed Nov 28, 2015
2 parents a36e147 + 988b3a8 commit ed77cda
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 15 deletions.
56 changes: 47 additions & 9 deletions ghooks.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
package ghooks

import (
"bytes"
"crypto/hmac"
"crypto/sha1"
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strconv"
"strings"
)
Expand All @@ -13,7 +19,8 @@ const (
)

type Server struct {
Port int
Port int
Secret string
}

type Hook struct {
Expand All @@ -40,16 +47,16 @@ func Emmit(name string, payload interface{}) {
}

func NewServer(port int) *Server {
return &Server{port}
return &Server{Port: port}
}

func (s *Server) Run() error {
fmt.Printf("ghooks server start 0.0.0.0:%d \n", s.Port)
http.HandleFunc("/", Reciver)
http.HandleFunc("/", s.Reciver)
return http.ListenAndServe(":"+strconv.Itoa(s.Port), nil)
}

func Reciver(w http.ResponseWriter, req *http.Request) {
func (s *Server) Reciver(w http.ResponseWriter, req *http.Request) {
if req.Method == "GET" {
http.Error(w, "Method Not Allowd", http.StatusMethodNotAllowed)
return
Expand All @@ -68,29 +75,60 @@ func Reciver(w http.ResponseWriter, req *http.Request) {
}
defer req.Body.Close()

body, err := ioutil.ReadAll(req.Body)
if err != nil {
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}

if s.Secret != "" {
signature := req.Header.Get("X-Hub-Signature")
if !s.isValidSignature(body, signature) {
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
}

var payload interface{}
var decoder *json.Decoder

if strings.Contains(req.Header.Get("Content-Type"), "application/json") {

decoder = json.NewDecoder(req.Body)
decoder = json.NewDecoder(bytes.NewReader(body))

} else if strings.Contains(req.Header.Get("Content-Type"), "application/x-www-form-urlencoded") {

err := req.ParseForm()
v, err := url.ParseQuery(string(body))
if err != nil {
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
p := req.FormValue("payload")
p := v.Get("payload")
decoder = json.NewDecoder(strings.NewReader(p))
}

err := decoder.Decode(&payload)
if err != nil {
if err := decoder.Decode(&payload); err != nil {
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
Emmit(event, payload)
w.WriteHeader(http.StatusOK)
}

func (s *Server) isValidSignature(body []byte, signature string) bool {

if !strings.HasPrefix(signature, "sha1=") {
return false
}

mac := hmac.New(sha1.New, []byte(s.Secret))
mac.Write(body)
actual := mac.Sum(nil)

expected, err := hex.DecodeString(signature[5:])
if err != nil {
return false
}

return hmac.Equal(actual, expected)
}
52 changes: 46 additions & 6 deletions ghooks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,23 +60,26 @@ func TestReciver(t *testing.T) {
}

w := httptest.NewRecorder()
Reciver(w, req)
s := &Server{}
s.Reciver(w, req)
if w.Code == 200 {
t.Fatalf("Allowd only POST Method but expected status 200; received %d", w.Code)
}

req, _ = http.NewRequest("POST", "/", nil)
req.Header.Add("X-GitHub-Event", "")
w = httptest.NewRecorder()
Reciver(w, req)
s = &Server{}
s.Reciver(w, req)
if w.Code == 200 {
t.Fatalf("Event name is nil but return 200; received %d", w.Code)
}

req, _ = http.NewRequest("POST", "/", nil)
req.Header.Set("X-GitHub-Event", "hoge")
w = httptest.NewRecorder()
Reciver(w, req)
s = &Server{}
s.Reciver(w, req)
if w.Code == 200 {
t.Fatalf("Body is nil but return 200; received %d", w.Code)
}
Expand All @@ -86,7 +89,8 @@ func TestReciver(t *testing.T) {
req.Header.Set("X-GitHub-Event", "hoge")
req.Header.Set("Content-Type", "application/json")
w = httptest.NewRecorder()
Reciver(w, req)
s = &Server{}
s.Reciver(w, req)
if w.Code != 200 {
t.Fatalf("Not return 200; received %d", w.Code)
}
Expand All @@ -96,7 +100,8 @@ func TestReciver(t *testing.T) {
req.Header.Set("X-GitHub-Event", "hoge")
req.Header.Set("Content-Type", "application/json")
w = httptest.NewRecorder()
Reciver(w, req)
s = &Server{}
s.Reciver(w, req)
if w.Code == 200 {
t.Fatalf("Should not be 200; received %d", w.Code)
}
Expand All @@ -106,9 +111,44 @@ func TestReciver(t *testing.T) {
req.Header.Set("X-GitHub-Event", "hoge")
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
w = httptest.NewRecorder()
Reciver(w, req)
s = &Server{}
s.Reciver(w, req)
if w.Code != 200 {
t.Fatalf("Not return 200; received %d", w.Code)
}

json_string = `{"fuga": "hoge", "foo": { "bar": "boo" }}`
req, _ = http.NewRequest("POST", "/", strings.NewReader(json_string))
req.Header.Set("X-GitHub-Event", "hoge")
req.Header.Set("Content-Type", "application/json")
w = httptest.NewRecorder()
s = &Server{Secret: "mysecret"}
s.Reciver(w, req)
if w.Code != 400 {
t.Fatalf("Not return 400; received %d", w.Code)
}

json_string = `{"fuga": "hoge", "foo": { "bar": "boo" }}`
req, _ = http.NewRequest("POST", "/", strings.NewReader(json_string))
req.Header.Set("X-GitHub-Event", "hoge")
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Hub-Signature", "sha1=invalid")
w = httptest.NewRecorder()
s = &Server{Secret: "dameleon"}
s.Reciver(w, req)
if w.Code != 400 {
t.Fatalf("Not return 400; received %d", w.Code)
}

json_string = `{"fuga": "hoge", "foo": { "bar": "boo" }}`
req, _ = http.NewRequest("POST", "/", strings.NewReader(json_string))
req.Header.Set("X-GitHub-Event", "hoge")
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Hub-Signature", "sha1=17f693f6f260c0e4b4090ae1e0cf195e03bed614")
w = httptest.NewRecorder()
s = &Server{Secret: "mysecret"}
s.Reciver(w, req)
if w.Code != 200 {
t.Fatalf("Not return 200; received %d", w.Code)
}
}

0 comments on commit ed77cda

Please sign in to comment.