B: Dynamically get Auth Secret
This commit is contained in:
parent
ebd193ab38
commit
d78ca20541
|
@ -35,6 +35,5 @@ func BuildRouter(r *gin.Engine) {
|
||||||
pcoClientMap = make(map[primitive.ObjectID]*pco.PcoApiClient)
|
pcoClientMap = make(map[primitive.ObjectID]*pco.PcoApiClient)
|
||||||
|
|
||||||
pco := r.Group("/pco")
|
pco := r.Group("/pco")
|
||||||
pco.Use(ValidatePcoWebhook)
|
pco.POST("/:userid", ValidatePcoWebhook, ConsumePcoWebhook)
|
||||||
pco.POST("/:userid", ConsumePcoWebhook)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,21 @@
|
||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"crypto/hmac"
|
"crypto/hmac"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"git.preston-baxter.com/Preston_PLB/capstone/frontend-service/config"
|
"git.preston-baxter.com/Preston_PLB/capstone/webhook-service/vendors/pco/webhooks"
|
||||||
"git.preston-baxter.com/Preston_PLB/capstone/frontend-service/db/models"
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/google/jsonapi"
|
||||||
)
|
)
|
||||||
|
|
||||||
const PCO_VALIDATE_HEADER = "X-PCO-Webhooks-Authenticity"
|
const PCO_VALIDATE_HEADER = "X-PCO-Webhooks-Authenticity"
|
||||||
|
|
||||||
func ValidatePcoWebhook(c *gin.Context) {
|
func ValidatePcoWebhook(c *gin.Context) {
|
||||||
conf := config.Config()
|
|
||||||
|
|
||||||
//get remote version from header
|
//get remote version from header
|
||||||
remoteDigestStr := c.GetHeader(PCO_VALIDATE_HEADER)
|
remoteDigestStr := c.GetHeader(PCO_VALIDATE_HEADER)
|
||||||
if remoteDigestStr == "" {
|
if remoteDigestStr == "" {
|
||||||
|
@ -26,18 +25,28 @@ func ValidatePcoWebhook(c *gin.Context) {
|
||||||
}
|
}
|
||||||
pcoSig := make([]byte, len(remoteDigestStr)/2)
|
pcoSig := make([]byte, len(remoteDigestStr)/2)
|
||||||
_, err := hex.Decode(pcoSig, []byte(remoteDigestStr))
|
_, err := hex.Decode(pcoSig, []byte(remoteDigestStr))
|
||||||
|
if err != nil {
|
||||||
|
log.WithError(err).Error("Failed to decode byte digest")
|
||||||
|
_ = c.AbortWithError(501, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
//clone request to harmlessly inspect the body
|
//clone request to harmlessly inspect the body
|
||||||
bodyReader := c.Request.Clone(context.Background()).Body
|
bodyReader := c.Request.Clone(context.Background()).Body
|
||||||
body, err := io.ReadAll(bodyReader)
|
body, err := io.ReadAll(bodyReader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Error("Failed to read body while validating PCO webhook")
|
log.WithError(err).Error("Failed to read body while validating PCO webhook")
|
||||||
c.AbortWithError(501, err)
|
_ = c.AbortWithError(501, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//Get secret
|
//Get secret
|
||||||
key := conf.Vendors[models.PCO_VENDOR_NAME].WebhookSecret
|
key, err := getAuthSecret(c, body)
|
||||||
|
if err != nil {
|
||||||
|
log.WithError(err).Error("Failed to find auth secret for event. It may not be setup")
|
||||||
|
_ = c.AbortWithError(501, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
//Get HMAC
|
//Get HMAC
|
||||||
hmacSig := hmac.New(sha256.New, []byte(key))
|
hmacSig := hmac.New(sha256.New, []byte(key))
|
||||||
|
@ -48,3 +57,20 @@ func ValidatePcoWebhook(c *gin.Context) {
|
||||||
c.AbortWithStatus(401)
|
c.AbortWithStatus(401)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getAuthSecret(c *gin.Context, body []byte) (string, error) {
|
||||||
|
userObjectId := userIdFromContext(c)
|
||||||
|
|
||||||
|
event := &webhooks.EventDelivery{}
|
||||||
|
err := jsonapi.UnmarshalPayload(bytes.NewBuffer(body), event)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
webhook, err := mongo.FindPcoSubscriptionForUser(*userObjectId, event.Name)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return webhook.Details.AuthenticitySecret, nil
|
||||||
|
}
|
||||||
|
|
|
@ -28,34 +28,47 @@ var (
|
||||||
|
|
||||||
type actionFunc func(*gin.Context, *webhooks.EventDelivery) error
|
type actionFunc func(*gin.Context, *webhooks.EventDelivery) error
|
||||||
|
|
||||||
|
func userIdFromContext(c *gin.Context) (*primitive.ObjectID) {
|
||||||
|
if id, ok := c.Get("user_bson_id"); !ok {
|
||||||
|
userId := c.Param("userid")
|
||||||
|
|
||||||
|
if userId == "" {
|
||||||
|
log.Warn("Webhook did not contain user id. Rejecting")
|
||||||
|
c.AbortWithStatus(404)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
userObjectId, err := primitive.ObjectIDFromHex(userId)
|
||||||
|
if err != nil {
|
||||||
|
log.WithError(err).Warn("User Id was malformed")
|
||||||
|
c.AbortWithStatus(400)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
c.Set("user_bson_id", userObjectId)
|
||||||
|
return &userObjectId
|
||||||
|
} else {
|
||||||
|
if objId, ok := id.(primitive.ObjectID); ok {
|
||||||
|
return &objId
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func ConsumePcoWebhook(c *gin.Context) {
|
func ConsumePcoWebhook(c *gin.Context) {
|
||||||
userId := c.Param("userid")
|
userObjectId := userIdFromContext(c)
|
||||||
|
|
||||||
if userId == "" {
|
|
||||||
log.Warn("Webhook did not contain user id. Rejecting")
|
|
||||||
c.AbortWithStatus(404)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//get actions for user
|
|
||||||
userObjectId, err := primitive.ObjectIDFromHex(userId)
|
|
||||||
if err != nil {
|
|
||||||
log.WithError(err).Warn("User Id was malformed")
|
|
||||||
c.AbortWithStatus(400)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.Set("user_bson_id", userObjectId)
|
|
||||||
|
|
||||||
//read body and handle io in parallel because IO shenanigains
|
//read body and handle io in parallel because IO shenanigains
|
||||||
wg := new(sync.WaitGroup)
|
wg := new(sync.WaitGroup)
|
||||||
wg.Add(2)
|
wg.Add(2)
|
||||||
|
|
||||||
|
//get actions for user
|
||||||
var actionMappings []models.ActionMapping
|
var actionMappings []models.ActionMapping
|
||||||
var webhookBody *webhooks.EventDelivery
|
var webhookBody *webhooks.EventDelivery
|
||||||
errs := make([]error, 2)
|
errs := make([]error, 2)
|
||||||
|
|
||||||
go func(wg *sync.WaitGroup) {
|
go func(wg *sync.WaitGroup) {
|
||||||
actionMappings, errs[0] = mongo.FindActionMappingsByUser(userObjectId)
|
actionMappings, errs[0] = mongo.FindActionMappingsByUser(*userObjectId)
|
||||||
wg.Done()
|
wg.Done()
|
||||||
}(wg)
|
}(wg)
|
||||||
|
|
||||||
|
@ -81,7 +94,7 @@ func ConsumePcoWebhook(c *gin.Context) {
|
||||||
actionKey := fmt.Sprintf("%s:%s", mapping.Action.VendorName, mapping.Action.Type)
|
actionKey := fmt.Sprintf("%s:%s", mapping.Action.VendorName, mapping.Action.Type)
|
||||||
//if function exists run the function
|
//if function exists run the function
|
||||||
if action, ok := actionFuncMap[actionKey]; ok {
|
if action, ok := actionFuncMap[actionKey]; ok {
|
||||||
err = action(c, webhookBody)
|
err := action(c, webhookBody)
|
||||||
//handle error
|
//handle error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Errorf("Failed to execute action: %s. From event source: %s:%s", actionKey, mapping.SourceEvent.VendorName, mapping.SourceEvent.Key)
|
log.WithError(err).Errorf("Failed to execute action: %s. From event source: %s:%s", actionKey, mapping.SourceEvent.VendorName, mapping.SourceEvent.Key)
|
||||||
|
|
Loading…
Reference in New Issue