123 lines
3.2 KiB
Go
123 lines
3.2 KiB
Go
|
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)
|
||
|
}
|