mirror of
https://github.com/adnanh/webhook.git
synced 2025-05-12 08:34:43 +00:00
Merge pull request #13 from moorereason/refactor-helpers
Refactor to remove helpers package
This commit is contained in:
commit
84ce6f262a
4 changed files with 77 additions and 84 deletions
|
@ -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
|
||||
}
|
64
hook/hook.go
64
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)
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package helpers
|
||||
package hook
|
||||
|
||||
import "testing"
|
||||
|
20
webhook.go
20
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
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue