mirror of
https://github.com/adnanh/webhook.git
synced 2025-06-01 02:02:28 +00:00
Merge pull request #384 from moorereason/feature/value-walk
Show failed parameter node lookups
This commit is contained in:
commit
dc4f42bb26
3 changed files with 113 additions and 79 deletions
|
@ -47,6 +47,28 @@ const (
|
|||
EnvNamespace string = "HOOK_"
|
||||
)
|
||||
|
||||
// ParameterNodeError describes an error walking a parameter node.
|
||||
type ParameterNodeError struct {
|
||||
key string
|
||||
}
|
||||
|
||||
func (e *ParameterNodeError) Error() string {
|
||||
if e == nil {
|
||||
return "<nil>"
|
||||
}
|
||||
return fmt.Sprintf("parameter node not found: %s", e.key)
|
||||
}
|
||||
|
||||
// IsParameterNodeError returns whether err is of type ParameterNodeError.
|
||||
func IsParameterNodeError(err error) bool {
|
||||
switch err.(type) {
|
||||
case *ParameterNodeError:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// SignatureError describes an invalid payload signature passed to Hook.
|
||||
type SignatureError struct {
|
||||
Signature string
|
||||
|
@ -307,9 +329,9 @@ func ReplaceParameter(s string, params interface{}, value interface{}) bool {
|
|||
}
|
||||
|
||||
// GetParameter extracts interface{} value based on the passed string
|
||||
func GetParameter(s string, params interface{}) (interface{}, bool) {
|
||||
func GetParameter(s string, params interface{}) (interface{}, error) {
|
||||
if params == nil {
|
||||
return nil, false
|
||||
return nil, errors.New("no parameters")
|
||||
}
|
||||
|
||||
paramsValue := reflect.ValueOf(params)
|
||||
|
@ -323,7 +345,7 @@ func GetParameter(s string, params interface{}) (interface{}, bool) {
|
|||
index, err := strconv.ParseUint(p[0], 10, 64)
|
||||
|
||||
if err != nil || paramsValueSliceLength <= int(index) {
|
||||
return nil, false
|
||||
return nil, &ParameterNodeError{s}
|
||||
}
|
||||
|
||||
return GetParameter(p[1], params.([]interface{})[index])
|
||||
|
@ -332,18 +354,18 @@ func GetParameter(s string, params interface{}) (interface{}, bool) {
|
|||
index, err := strconv.ParseUint(s, 10, 64)
|
||||
|
||||
if err != nil || paramsValueSliceLength <= int(index) {
|
||||
return nil, false
|
||||
return nil, &ParameterNodeError{s}
|
||||
}
|
||||
|
||||
return params.([]interface{})[index], true
|
||||
return params.([]interface{})[index], nil
|
||||
}
|
||||
|
||||
return nil, false
|
||||
return nil, &ParameterNodeError{s}
|
||||
|
||||
case reflect.Map:
|
||||
// Check for raw key
|
||||
if v, ok := params.(map[string]interface{})[s]; ok {
|
||||
return v, true
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// Checked for dotted references
|
||||
|
@ -353,19 +375,21 @@ func GetParameter(s string, params interface{}) (interface{}, bool) {
|
|||
return GetParameter(p[1], pValue)
|
||||
}
|
||||
|
||||
return pValue, true
|
||||
return pValue, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, false
|
||||
return nil, &ParameterNodeError{s}
|
||||
}
|
||||
|
||||
// ExtractParameterAsString extracts value from interface{} as string based on the passed string
|
||||
func ExtractParameterAsString(s string, params interface{}) (string, bool) {
|
||||
if pValue, ok := GetParameter(s, params); ok {
|
||||
return fmt.Sprintf("%v", pValue), true
|
||||
func ExtractParameterAsString(s string, params interface{}) (string, error) {
|
||||
pValue, err := GetParameter(s, params)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return "", false
|
||||
|
||||
return fmt.Sprintf("%v", pValue), nil
|
||||
}
|
||||
|
||||
// Argument type specifies the parameter key name and the source it should
|
||||
|
@ -379,7 +403,7 @@ type Argument struct {
|
|||
|
||||
// Get Argument method returns the value for the Argument's key name
|
||||
// based on the Argument's source
|
||||
func (ha *Argument) Get(headers, query, payload *map[string]interface{}) (string, bool) {
|
||||
func (ha *Argument) Get(headers, query, payload *map[string]interface{}) (string, error) {
|
||||
var source *map[string]interface{}
|
||||
key := ha.Name
|
||||
|
||||
|
@ -392,35 +416,35 @@ func (ha *Argument) Get(headers, query, payload *map[string]interface{}) (string
|
|||
case SourcePayload:
|
||||
source = payload
|
||||
case SourceString:
|
||||
return ha.Name, true
|
||||
return ha.Name, nil
|
||||
case SourceEntirePayload:
|
||||
r, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return "", false
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(r), true
|
||||
return string(r), nil
|
||||
case SourceEntireHeaders:
|
||||
r, err := json.Marshal(headers)
|
||||
if err != nil {
|
||||
return "", false
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(r), true
|
||||
return string(r), nil
|
||||
case SourceEntireQuery:
|
||||
r, err := json.Marshal(query)
|
||||
if err != nil {
|
||||
return "", false
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(r), true
|
||||
return string(r), nil
|
||||
}
|
||||
|
||||
if source != nil {
|
||||
return ExtractParameterAsString(key, *source)
|
||||
}
|
||||
|
||||
return "", false
|
||||
return "", errors.New("no source for value retrieval")
|
||||
}
|
||||
|
||||
// Header is a structure containing header name and it's value
|
||||
|
@ -502,7 +526,10 @@ func (h *Hook) ParseJSONParameters(headers, query, payload *map[string]interface
|
|||
errors := make([]error, 0)
|
||||
|
||||
for i := range h.JSONStringParameters {
|
||||
if arg, ok := h.JSONStringParameters[i].Get(headers, query, payload); ok {
|
||||
arg, err := h.JSONStringParameters[i].Get(headers, query, payload)
|
||||
if err != nil {
|
||||
errors = append(errors, &ArgumentError{h.JSONStringParameters[i]})
|
||||
} else {
|
||||
var newArg map[string]interface{}
|
||||
|
||||
decoder := json.NewDecoder(strings.NewReader(string(arg)))
|
||||
|
@ -536,8 +563,6 @@ func (h *Hook) ParseJSONParameters(headers, query, payload *map[string]interface
|
|||
} else {
|
||||
errors = append(errors, &SourceError{h.JSONStringParameters[i]})
|
||||
}
|
||||
} else {
|
||||
errors = append(errors, &ArgumentError{h.JSONStringParameters[i]})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -557,12 +582,14 @@ func (h *Hook) ExtractCommandArguments(headers, query, payload *map[string]inter
|
|||
args = append(args, h.ExecuteCommand)
|
||||
|
||||
for i := range h.PassArgumentsToCommand {
|
||||
if arg, ok := h.PassArgumentsToCommand[i].Get(headers, query, payload); ok {
|
||||
args = append(args, arg)
|
||||
} else {
|
||||
arg, err := h.PassArgumentsToCommand[i].Get(headers, query, payload)
|
||||
if err != nil {
|
||||
args = append(args, "")
|
||||
errors = append(errors, &ArgumentError{h.PassArgumentsToCommand[i]})
|
||||
continue
|
||||
}
|
||||
|
||||
args = append(args, arg)
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
|
@ -579,16 +606,18 @@ func (h *Hook) ExtractCommandArgumentsForEnv(headers, query, payload *map[string
|
|||
args := make([]string, 0)
|
||||
errors := make([]error, 0)
|
||||
for i := range h.PassEnvironmentToCommand {
|
||||
if arg, ok := h.PassEnvironmentToCommand[i].Get(headers, query, payload); ok {
|
||||
if h.PassEnvironmentToCommand[i].EnvName != "" {
|
||||
// first try to use the EnvName if specified
|
||||
args = append(args, h.PassEnvironmentToCommand[i].EnvName+"="+arg)
|
||||
} else {
|
||||
// then fallback on the name
|
||||
args = append(args, EnvNamespace+h.PassEnvironmentToCommand[i].Name+"="+arg)
|
||||
}
|
||||
} else {
|
||||
arg, err := h.PassEnvironmentToCommand[i].Get(headers, query, payload)
|
||||
if err != nil {
|
||||
errors = append(errors, &ArgumentError{h.PassEnvironmentToCommand[i]})
|
||||
continue
|
||||
}
|
||||
|
||||
if h.PassEnvironmentToCommand[i].EnvName != "" {
|
||||
// first try to use the EnvName if specified
|
||||
args = append(args, h.PassEnvironmentToCommand[i].EnvName+"="+arg)
|
||||
} else {
|
||||
// then fallback on the name
|
||||
args = append(args, EnvNamespace+h.PassEnvironmentToCommand[i].Name+"="+arg)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -613,30 +642,30 @@ func (h *Hook) ExtractCommandArgumentsForFile(headers, query, payload *map[strin
|
|||
args := make([]FileParameter, 0)
|
||||
errors := make([]error, 0)
|
||||
for i := range h.PassFileToCommand {
|
||||
if arg, ok := h.PassFileToCommand[i].Get(headers, query, payload); ok {
|
||||
|
||||
if h.PassFileToCommand[i].EnvName == "" {
|
||||
// if no environment-variable name is set, fall-back on the name
|
||||
log.Printf("no ENVVAR name specified, falling back to [%s]", EnvNamespace+strings.ToUpper(h.PassFileToCommand[i].Name))
|
||||
h.PassFileToCommand[i].EnvName = EnvNamespace + strings.ToUpper(h.PassFileToCommand[i].Name)
|
||||
}
|
||||
|
||||
var fileContent []byte
|
||||
if h.PassFileToCommand[i].Base64Decode {
|
||||
dec, err := base64.StdEncoding.DecodeString(arg)
|
||||
if err != nil {
|
||||
log.Printf("error decoding string [%s]", err)
|
||||
}
|
||||
fileContent = []byte(dec)
|
||||
} else {
|
||||
fileContent = []byte(arg)
|
||||
}
|
||||
|
||||
args = append(args, FileParameter{EnvName: h.PassFileToCommand[i].EnvName, Data: fileContent})
|
||||
|
||||
} else {
|
||||
arg, err := h.PassFileToCommand[i].Get(headers, query, payload)
|
||||
if err != nil {
|
||||
errors = append(errors, &ArgumentError{h.PassFileToCommand[i]})
|
||||
continue
|
||||
}
|
||||
|
||||
if h.PassFileToCommand[i].EnvName == "" {
|
||||
// if no environment-variable name is set, fall-back on the name
|
||||
log.Printf("no ENVVAR name specified, falling back to [%s]", EnvNamespace+strings.ToUpper(h.PassFileToCommand[i].Name))
|
||||
h.PassFileToCommand[i].EnvName = EnvNamespace + strings.ToUpper(h.PassFileToCommand[i].Name)
|
||||
}
|
||||
|
||||
var fileContent []byte
|
||||
if h.PassFileToCommand[i].Base64Decode {
|
||||
dec, err := base64.StdEncoding.DecodeString(arg)
|
||||
if err != nil {
|
||||
log.Printf("error decoding string [%s]", err)
|
||||
}
|
||||
fileContent = []byte(dec)
|
||||
} else {
|
||||
fileContent = []byte(arg)
|
||||
}
|
||||
|
||||
args = append(args, FileParameter{EnvName: h.PassFileToCommand[i].EnvName, Data: fileContent})
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
|
@ -818,7 +847,8 @@ func (r MatchRule) Evaluate(headers, query, payload *map[string]interface{}, bod
|
|||
return CheckScalrSignature(*headers, *body, r.Secret, true)
|
||||
}
|
||||
|
||||
if arg, ok := r.Parameter.Get(headers, query, payload); ok {
|
||||
arg, err := r.Parameter.Get(headers, query, payload)
|
||||
if err == nil {
|
||||
switch r.Type {
|
||||
case MatchValue:
|
||||
return compare(arg, r.Value), nil
|
||||
|
@ -835,7 +865,7 @@ func (r MatchRule) Evaluate(headers, query, payload *map[string]interface{}, bod
|
|||
return err == nil, err
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
return false, err
|
||||
}
|
||||
|
||||
// compare is a helper function for constant time string comparisons.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue