From 7dd55f52326163923d0777bd9a337e8d97d21213 Mon Sep 17 00:00:00 2001 From: Cameron Moore Date: Fri, 20 Mar 2015 08:55:42 -0500 Subject: [PATCH] Refactor to remove helpers package This commit removes the "helpers" package by moving functions from the package into the other packages that use them. CheckPayloadSignature() and ExtractParamater() are simply moved to the "hook" package. I'm not sure of the usefulness of having these functions exported, but I left them allow for now. ValuesToMap() is moved to the "main" webhook package and renamed to valuesToMap(). Tests were moved into the "hook" package since we only test ExtractParameter() right now. This commit closes adnanh/webhook#12. --- helpers/helpers.go | 75 -------------------- hook/hook.go | 64 +++++++++++++++-- helpers/helpers_test.go => hook/hook_test.go | 2 +- webhook.go | 20 ++++-- 4 files changed, 77 insertions(+), 84 deletions(-) delete mode 100644 helpers/helpers.go rename helpers/helpers_test.go => hook/hook_test.go (98%) diff --git a/helpers/helpers.go b/helpers/helpers.go deleted file mode 100644 index 9e5d88c..0000000 --- a/helpers/helpers.go +++ /dev/null @@ -1,75 +0,0 @@ -package helpers - -import ( - "crypto/hmac" - "crypto/sha1" - "encoding/hex" - "fmt" - "reflect" - "strconv" - "strings" -) - -// CheckPayloadSignature calculates and verifies SHA1 signature of the given payload -func CheckPayloadSignature(payload []byte, secret string, signature string) (string, bool) { - if strings.HasPrefix(signature, "sha1=") { - signature = signature[5:] - } - - mac := hmac.New(sha1.New, []byte(secret)) - mac.Write(payload) - expectedMAC := hex.EncodeToString(mac.Sum(nil)) - - return expectedMAC, hmac.Equal([]byte(signature), []byte(expectedMAC)) -} - -// ValuesToMap converts map[string][]string to a map[string]string object -func ValuesToMap(values map[string][]string) map[string]interface{} { - ret := make(map[string]interface{}) - - for key, value := range values { - if len(value) > 0 { - ret[key] = value[0] - } - } - - return ret -} - -// ExtractParameter extracts value from interface{} based on the passed string -func ExtractParameter(s string, params interface{}) (string, bool) { - if params == nil { - return "", false - } - - if paramsValue := reflect.ValueOf(params); paramsValue.Kind() == reflect.Slice { - if paramsValueSliceLength := paramsValue.Len(); paramsValueSliceLength > 0 { - - if p := strings.SplitN(s, ".", 2); len(p) > 1 { - index, err := strconv.ParseInt(p[0], 10, 64) - - if err != nil { - return "", false - } else if paramsValueSliceLength <= int(index) { - return "", false - } - - return ExtractParameter(p[1], params.([]interface{})[index]) - } - } - - return "", false - } - - if p := strings.SplitN(s, ".", 2); len(p) > 1 { - if pValue, ok := params.(map[string]interface{})[p[0]]; ok { - return ExtractParameter(p[1], pValue) - } - } else { - if pValue, ok := params.(map[string]interface{})[p[0]]; ok { - return fmt.Sprintf("%v", pValue), true - } - } - - return "", false -} diff --git a/hook/hook.go b/hook/hook.go index d0c4141..669c177 100644 --- a/hook/hook.go +++ b/hook/hook.go @@ -1,12 +1,17 @@ package hook import ( + "crypto/hmac" + "crypto/sha1" + "encoding/hex" "encoding/json" + "fmt" "io/ioutil" "log" + "reflect" "regexp" - - "github.com/adnanh/webhook/helpers" + "strconv" + "strings" ) // Constants used to specify the parameter source @@ -16,6 +21,57 @@ const ( SourcePayload string = "payload" ) +// CheckPayloadSignature calculates and verifies SHA1 signature of the given payload +func CheckPayloadSignature(payload []byte, secret string, signature string) (string, bool) { + if strings.HasPrefix(signature, "sha1=") { + signature = signature[5:] + } + + mac := hmac.New(sha1.New, []byte(secret)) + mac.Write(payload) + expectedMAC := hex.EncodeToString(mac.Sum(nil)) + + return expectedMAC, hmac.Equal([]byte(signature), []byte(expectedMAC)) +} + +// ExtractParameter extracts value from interface{} based on the passed string +func ExtractParameter(s string, params interface{}) (string, bool) { + if params == nil { + return "", false + } + + if paramsValue := reflect.ValueOf(params); paramsValue.Kind() == reflect.Slice { + if paramsValueSliceLength := paramsValue.Len(); paramsValueSliceLength > 0 { + + if p := strings.SplitN(s, ".", 2); len(p) > 1 { + index, err := strconv.ParseInt(p[0], 10, 64) + + if err != nil { + return "", false + } else if paramsValueSliceLength <= int(index) { + return "", false + } + + return ExtractParameter(p[1], params.([]interface{})[index]) + } + } + + return "", false + } + + if p := strings.SplitN(s, ".", 2); len(p) > 1 { + if pValue, ok := params.(map[string]interface{})[p[0]]; ok { + return ExtractParameter(p[1], pValue) + } + } else { + if pValue, ok := params.(map[string]interface{})[p[0]]; ok { + return fmt.Sprintf("%v", pValue), true + } + } + + return "", false +} + // Argument type specifies the parameter key name and the source it should // be extracted from type Argument struct { @@ -38,7 +94,7 @@ func (ha *Argument) Get(headers, query, payload *map[string]interface{}) (string } if source != nil { - return helpers.ExtractParameter(ha.Name, *source) + return ExtractParameter(ha.Name, *source) } return "", false @@ -201,7 +257,7 @@ func (r MatchRule) Evaluate(headers, query, payload *map[string]interface{}, bod } return ok case MatchHashSHA1: - expected, ok := helpers.CheckPayloadSignature(*body, r.Secret, arg) + expected, ok := CheckPayloadSignature(*body, r.Secret, arg) if !ok { log.Printf("payload signature mismatch, expected %s got %s", expected, arg) } diff --git a/helpers/helpers_test.go b/hook/hook_test.go similarity index 98% rename from helpers/helpers_test.go rename to hook/hook_test.go index 61a54bb..810f883 100644 --- a/helpers/helpers_test.go +++ b/hook/hook_test.go @@ -1,4 +1,4 @@ -package helpers +package hook import "testing" diff --git a/webhook.go b/webhook.go index 0bf870a..7f1949a 100644 --- a/webhook.go +++ b/webhook.go @@ -12,7 +12,6 @@ import ( "os/exec" "strings" - "github.com/adnanh/webhook/helpers" "github.com/adnanh/webhook/hook" "github.com/codegangsta/negroni" @@ -143,10 +142,10 @@ func hookHandler(w http.ResponseWriter, r *http.Request) { } // parse headers - headers := helpers.ValuesToMap(r.Header) + headers := valuesToMap(r.Header) // parse query variables - query := helpers.ValuesToMap(r.URL.Query()) + query := valuesToMap(r.URL.Query()) // parse body var payload map[string]interface{} @@ -167,7 +166,7 @@ func hookHandler(w http.ResponseWriter, r *http.Request) { if err != nil { log.Printf("error parsing form payload %+v\n", err) } else { - payload = helpers.ValuesToMap(fd) + payload = valuesToMap(fd) } } @@ -236,3 +235,16 @@ func watchForFileChange() { } } } + +// valuesToMap converts map[string][]string to a map[string]string object +func valuesToMap(values map[string][]string) map[string]interface{} { + ret := make(map[string]interface{}) + + for key, value := range values { + if len(value) > 0 { + ret[key] = value[0] + } + } + + return ret +}