1
0
Fork 0
telegraf/plugins/inputs/webhooks/artifactory/artifactory_webhook.go

123 lines
3.2 KiB
Go
Raw Permalink Normal View History

package artifactory
import (
"crypto/hmac"
"crypto/sha1" //nolint:gosec // G505: Blocklisted import crypto/sha1: weak cryptographic primitive - sha1 hash is what is desired in this case
"encoding/hex"
"encoding/json"
"fmt"
"io"
"net/http"
"github.com/gorilla/mux"
"github.com/influxdata/telegraf"
)
type Webhook struct {
Path string
Secret string
acc telegraf.Accumulator
log telegraf.Logger
}
// Register registers the webhook with the provided router
func (awh *Webhook) Register(router *mux.Router, acc telegraf.Accumulator, log telegraf.Logger) {
router.HandleFunc(awh.Path, awh.eventHandler).Methods("POST")
awh.log = log
awh.log.Infof("Started webhooks_artifactory on %s", awh.Path)
awh.acc = acc
}
func (awh *Webhook) eventHandler(rw http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
data, err := io.ReadAll(r.Body)
if err != nil {
rw.WriteHeader(http.StatusBadRequest)
return
}
if awh.Secret != "" && !checkSignature(awh.Secret, data, r.Header.Get("x-jfrog-event-auth")) {
awh.log.Error("Failed to check the artifactory webhook auth signature")
rw.WriteHeader(http.StatusBadRequest)
return
}
bodyFields := make(map[string]interface{})
err = json.Unmarshal(data, &bodyFields)
if err != nil {
rw.WriteHeader(http.StatusBadRequest)
}
et := fmt.Sprintf("%v", bodyFields["event_type"])
ed := fmt.Sprintf("%v", bodyFields["domain"])
ne, err := awh.newEvent(data, et, ed)
if err != nil {
rw.WriteHeader(http.StatusBadRequest)
}
if ne != nil {
nm := ne.newMetric()
awh.acc.AddFields("artifactory_webhooks", nm.Fields(), nm.Tags(), nm.Time())
}
rw.WriteHeader(http.StatusOK)
}
type newEventError struct {
s string
}
func (e *newEventError) Error() string {
return e.s
}
func (awh *Webhook) newEvent(data []byte, et, ed string) (event, error) {
awh.log.Debugf("New %v domain %v event received", ed, et)
switch ed {
case "artifact":
if et == "deployed" || et == "deleted" {
return generateEvent(data, &artifactDeploymentOrDeletedEvent{})
}
if et == "moved" || et == "copied" {
return generateEvent(data, &artifactMovedOrCopiedEvent{})
}
return nil, &newEventError{"Not a recognized event type"}
case "artifact_property":
return generateEvent(data, &artifactPropertiesEvent{})
case "docker":
return generateEvent(data, &dockerEvent{})
case "build":
return generateEvent(data, &buildEvent{})
case "release_bundle":
return generateEvent(data, &releaseBundleEvent{})
case "distribution":
return generateEvent(data, &distributionEvent{})
case "destination":
return generateEvent(data, &destinationEvent{})
}
return nil, &newEventError{"Not a recognized event type"}
}
func generateEvent(data []byte, event event) (event, error) {
err := json.Unmarshal(data, event)
if err != nil {
return nil, err
}
return event, nil
}
func checkSignature(secret string, data []byte, signature string) bool {
return hmac.Equal([]byte(signature), []byte(generateSignature(secret, data)))
}
func generateSignature(secret string, data []byte) string {
mac := hmac.New(sha1.New, []byte(secret))
if _, err := mac.Write(data); err != nil {
return err.Error()
}
result := mac.Sum(nil)
return "sha1=" + hex.EncodeToString(result)
}