mirror of https://github.com/harness/drone.git
feat: [CODE-2500]: add support for webhook internal url (#2806)
* feat: [CODE-2500]: add support for webhook internal url * feat: [CODE-2500]: add support for webhook internal url * feat: [CODE-2500]: add support for webhook internal urlpull/3576/head
parent
ddf3f1d3b9
commit
f7411b41c9
|
@ -33,7 +33,12 @@ const (
|
|||
var ErrInternalWebhookOperationNotAllowed = usererror.Forbidden("changes to internal webhooks are not allowed")
|
||||
|
||||
// CheckURL validates the url of a webhook.
|
||||
func CheckURL(rawURL string, allowLoopback bool, allowPrivateNetwork bool) error {
|
||||
func CheckURL(rawURL string, allowLoopback bool, allowPrivateNetwork bool, internal bool) error {
|
||||
// for internal webhooks skip URL validation as it is not used
|
||||
if internal {
|
||||
return nil
|
||||
}
|
||||
|
||||
// check URL
|
||||
if len(rawURL) > webhookMaxURLLength {
|
||||
return check.NewValidationErrorf("The URL of a webhook can be at most %d characters long.",
|
||||
|
|
|
@ -56,7 +56,7 @@ func (c *Controller) Create(
|
|||
internal bool,
|
||||
) (*types.Webhook, error) {
|
||||
// validate input
|
||||
err := sanitizeCreateInput(in, c.allowLoopback, c.allowPrivateNetwork || internal)
|
||||
err := sanitizeCreateInput(in, c.allowLoopback, c.allowPrivateNetwork, internal)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -121,7 +121,12 @@ func (c *Controller) Create(
|
|||
return hook, nil
|
||||
}
|
||||
|
||||
func sanitizeCreateInput(in *CreateInput, allowLoopback bool, allowPrivateNetwork bool) error {
|
||||
func sanitizeCreateInput(
|
||||
in *CreateInput,
|
||||
allowLoopback bool,
|
||||
allowPrivateNetwork bool,
|
||||
internal bool,
|
||||
) error {
|
||||
// TODO [CODE-1363]: remove after identifier migration.
|
||||
if in.Identifier == "" {
|
||||
in.Identifier = in.UID
|
||||
|
@ -149,7 +154,7 @@ func sanitizeCreateInput(in *CreateInput, allowLoopback bool, allowPrivateNetwor
|
|||
if err := check.Description(in.Description); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := CheckURL(in.URL, allowLoopback, allowPrivateNetwork); err != nil {
|
||||
if err := CheckURL(in.URL, allowLoopback, allowPrivateNetwork, internal); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := checkSecret(in.Secret); err != nil {
|
||||
|
|
|
@ -125,7 +125,8 @@ func sanitizeUpdateInput(in *UpdateInput, allowLoopback bool, allowPrivateNetwor
|
|||
}
|
||||
}
|
||||
if in.URL != nil {
|
||||
if err := CheckURL(*in.URL, allowLoopback, allowPrivateNetwork); err != nil {
|
||||
// internal is set to false as internal webhooks cannot be updated
|
||||
if err := CheckURL(*in.URL, allowLoopback, allowPrivateNetwork, false); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -117,7 +117,7 @@ func sanitizeWebhook(
|
|||
return err
|
||||
}
|
||||
|
||||
if err := webhookpkg.CheckURL(in.Target, allowLoopback, allowPrivateNetwork); err != nil {
|
||||
if err := webhookpkg.CheckURL(in.Target, allowLoopback, allowPrivateNetwork, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -47,6 +47,9 @@ type Config struct {
|
|||
MaxRetries int
|
||||
AllowPrivateNetwork bool
|
||||
AllowLoopback bool
|
||||
|
||||
// InternalWebhooksURL specifies the internal webhook URL which will be used if webhook is marked internal
|
||||
InternalWebhooksURL string
|
||||
}
|
||||
|
||||
func (c *Config) Prepare() error {
|
||||
|
|
|
@ -285,8 +285,7 @@ func (s *Service) executeWebhook(ctx context.Context, webhook *types.Webhook, tr
|
|||
// NOTE: if the body is an io.Reader, the value is used as response body as is, otherwise it'll be JSON serialized.
|
||||
func (s *Service) prepareHTTPRequest(ctx context.Context, execution *types.WebhookExecution,
|
||||
triggerType enum.WebhookTrigger, webhook *types.Webhook, body any) (*http.Request, error) {
|
||||
// set URL as is (already has been validated, any other error will be caught in request creation)
|
||||
execution.Request.URL = webhook.URL
|
||||
execution.Request.URL = s.getWebhookURL(ctx, webhook)
|
||||
|
||||
// Serialize body before anything else.
|
||||
// This allows the user to retrigger the execution even in case of bad URL.
|
||||
|
@ -369,6 +368,19 @@ func (s *Service) prepareHTTPRequest(ctx context.Context, execution *types.Webho
|
|||
return req, nil
|
||||
}
|
||||
|
||||
func (s *Service) getWebhookURL(ctx context.Context, webhook *types.Webhook) string {
|
||||
// in case a webhook is internal use the specified URL if not empty instead of using one saved in db.
|
||||
if webhook.Internal && s.config.InternalWebhooksURL != "" {
|
||||
return s.config.InternalWebhooksURL
|
||||
}
|
||||
|
||||
if webhook.Internal && s.config.InternalWebhooksURL == "" {
|
||||
log.Ctx(ctx).Error().Msg("internal webhook URL is empty, falling back to one in db")
|
||||
}
|
||||
// set URL as is (already has been validated, any other error will be caught in request creation)
|
||||
return webhook.URL
|
||||
}
|
||||
|
||||
func (s *Service) toXHeader(name string) string {
|
||||
return fmt.Sprintf("X-%s-%s", s.config.HeaderIdentity, name)
|
||||
}
|
||||
|
|
|
@ -331,6 +331,7 @@ func ProvideWebhookConfig(config *types.Config) webhook.Config {
|
|||
MaxRetries: config.Webhook.MaxRetries,
|
||||
AllowPrivateNetwork: config.Webhook.AllowPrivateNetwork,
|
||||
AllowLoopback: config.Webhook.AllowLoopback,
|
||||
InternalWebhooksURL: config.Webhook.InternalWebhooksURL,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -339,6 +339,8 @@ type Config struct {
|
|||
AllowLoopback bool `envconfig:"GITNESS_WEBHOOK_ALLOW_LOOPBACK" default:"false"`
|
||||
// RetentionTime is the duration after which webhook executions will be purged from the DB.
|
||||
RetentionTime time.Duration `envconfig:"GITNESS_WEBHOOK_RETENTION_TIME" default:"168h"` // 7 days
|
||||
// InternalWebhooksURL is the url for webhooks which are marked as internal
|
||||
InternalWebhooksURL string `envconfig:"GITNESS_WEBHOOK_INTERNAL_WEBHOOKS_URL"`
|
||||
}
|
||||
|
||||
Trigger struct {
|
||||
|
|
Loading…
Reference in New Issue