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.
This commit is contained in:
Cameron Moore 2015-03-20 08:55:42 -05:00
parent d8a21582a3
commit 7dd55f5232
4 changed files with 77 additions and 84 deletions

View file

@ -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)
}

29
hook/hook_test.go Normal file
View file

@ -0,0 +1,29 @@
package hook
import "testing"
var extractParameterTests = []struct {
s string
params map[string]interface{}
value string
ok bool
}{
{"a", map[string]interface{}{"a": "z"}, "z", true},
{"a.b", map[string]interface{}{"a": map[string]interface{}{"b": "z"}}, "z", true},
{"a.b.c", map[string]interface{}{"a": map[string]interface{}{"b": map[string]interface{}{"c": "z"}}}, "z", true},
{"a.1.b", map[string]interface{}{"a": []interface{}{map[string]interface{}{"b": "y"}, map[string]interface{}{"b": "z"}}}, "z", true},
{"a.1.b.c", map[string]interface{}{"a": []interface{}{map[string]interface{}{"b": map[string]interface{}{"c": "y"}}, map[string]interface{}{"b": map[string]interface{}{"c": "z"}}}}, "z", true},
// failures
{"a.X", map[string]interface{}{"a": map[string]interface{}{"b": "z"}}, "", false},
{"a.500.b", map[string]interface{}{"a": map[string]interface{}{"b": "z"}}, "", false},
{"a.501.b", map[string]interface{}{"a": []interface{}{map[string]interface{}{"b": "y"}, map[string]interface{}{"b": "z"}}}, "", false},
}
func TestExtractParameter(t *testing.T) {
for _, tt := range extractParameterTests {
s, ok := ExtractParameter(tt.s, tt.params)
if ok != tt.ok || s != tt.value {
t.Errorf("failed to extract parameter %q:\nexpected {value:%#v, ok:%#v},\ngot {value:%#v, ok:%#v}", tt.s, tt.value, tt.ok, s, ok)
}
}
}