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 +}