Refactor Webhook + Add X-Hub-Signature (#16176)
This PR removes multiple unneeded fields from the `HookTask` struct and adds the two headers `X-Hub-Signature` and `X-Hub-Signature-256`. ## ⚠️ BREAKING ⚠️ * The `Secret` field is no longer passed as part of the payload. * "Breaking" change (or fix?): The webhook history shows the real called url and not the url registered in the webhook (`deliver.go`@129). Close #16115 Fixes #7788 Fixes #11755 Co-authored-by: zeripath <art27@cantab.net>
This commit is contained in:
		
							parent
							
								
									0b27b93728
								
							
						
					
					
						commit
						9b1b4b5433
					
				
					 18 changed files with 130 additions and 179 deletions
				
			
		| 
						 | 
					@ -323,6 +323,8 @@ var migrations = []Migration{
 | 
				
			||||||
	NewMigration("Add new table repo_archiver", addRepoArchiver),
 | 
						NewMigration("Add new table repo_archiver", addRepoArchiver),
 | 
				
			||||||
	// v186 -> v187
 | 
						// v186 -> v187
 | 
				
			||||||
	NewMigration("Create protected tag table", createProtectedTagTable),
 | 
						NewMigration("Create protected tag table", createProtectedTagTable),
 | 
				
			||||||
 | 
						// v187 -> v188
 | 
				
			||||||
 | 
						NewMigration("Drop unneeded webhook related columns", dropWebhookColumns),
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetCurrentDBVersion returns the current db version
 | 
					// GetCurrentDBVersion returns the current db version
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										46
									
								
								models/migrations/v187.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								models/migrations/v187.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,46 @@
 | 
				
			||||||
 | 
					// Copyright 2021 The Gitea Authors. All rights reserved.
 | 
				
			||||||
 | 
					// Use of this source code is governed by a MIT-style
 | 
				
			||||||
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package migrations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"xorm.io/xorm"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func dropWebhookColumns(x *xorm.Engine) error {
 | 
				
			||||||
 | 
						// Make sure the columns exist before dropping them
 | 
				
			||||||
 | 
						type Webhook struct {
 | 
				
			||||||
 | 
							Signature string `xorm:"TEXT"`
 | 
				
			||||||
 | 
							IsSSL     bool   `xorm:"is_ssl"`
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := x.Sync2(new(Webhook)); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						type HookTask struct {
 | 
				
			||||||
 | 
							Typ         string `xorm:"VARCHAR(16) index"`
 | 
				
			||||||
 | 
							URL         string `xorm:"TEXT"`
 | 
				
			||||||
 | 
							Signature   string `xorm:"TEXT"`
 | 
				
			||||||
 | 
							HTTPMethod  string `xorm:"http_method"`
 | 
				
			||||||
 | 
							ContentType int
 | 
				
			||||||
 | 
							IsSSL       bool
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := x.Sync2(new(HookTask)); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sess := x.NewSession()
 | 
				
			||||||
 | 
						defer sess.Close()
 | 
				
			||||||
 | 
						if err := sess.Begin(); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := dropTableColumns(sess, "webhook", "signature", "is_ssl"); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := dropTableColumns(sess, "hook_task", "typ", "url", "signature", "http_method", "content_type", "is_ssl"); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return sess.Commit()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -109,6 +109,22 @@ type HookEvent struct {
 | 
				
			||||||
	HookEvents `json:"events"`
 | 
						HookEvents `json:"events"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// HookType is the type of a webhook
 | 
				
			||||||
 | 
					type HookType = string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Types of webhooks
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						GITEA    HookType = "gitea"
 | 
				
			||||||
 | 
						GOGS     HookType = "gogs"
 | 
				
			||||||
 | 
						SLACK    HookType = "slack"
 | 
				
			||||||
 | 
						DISCORD  HookType = "discord"
 | 
				
			||||||
 | 
						DINGTALK HookType = "dingtalk"
 | 
				
			||||||
 | 
						TELEGRAM HookType = "telegram"
 | 
				
			||||||
 | 
						MSTEAMS  HookType = "msteams"
 | 
				
			||||||
 | 
						FEISHU   HookType = "feishu"
 | 
				
			||||||
 | 
						MATRIX   HookType = "matrix"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// HookStatus is the status of a web hook
 | 
					// HookStatus is the status of a web hook
 | 
				
			||||||
type HookStatus int
 | 
					type HookStatus int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -126,15 +142,13 @@ type Webhook struct {
 | 
				
			||||||
	OrgID           int64 `xorm:"INDEX"`
 | 
						OrgID           int64 `xorm:"INDEX"`
 | 
				
			||||||
	IsSystemWebhook bool
 | 
						IsSystemWebhook bool
 | 
				
			||||||
	URL             string `xorm:"url TEXT"`
 | 
						URL             string `xorm:"url TEXT"`
 | 
				
			||||||
	Signature       string `xorm:"TEXT"`
 | 
					 | 
				
			||||||
	HTTPMethod      string `xorm:"http_method"`
 | 
						HTTPMethod      string `xorm:"http_method"`
 | 
				
			||||||
	ContentType     HookContentType
 | 
						ContentType     HookContentType
 | 
				
			||||||
	Secret          string `xorm:"TEXT"`
 | 
						Secret          string `xorm:"TEXT"`
 | 
				
			||||||
	Events          string `xorm:"TEXT"`
 | 
						Events          string `xorm:"TEXT"`
 | 
				
			||||||
	*HookEvent      `xorm:"-"`
 | 
						*HookEvent      `xorm:"-"`
 | 
				
			||||||
	IsSSL           bool         `xorm:"is_ssl"`
 | 
					 | 
				
			||||||
	IsActive        bool       `xorm:"INDEX"`
 | 
						IsActive        bool       `xorm:"INDEX"`
 | 
				
			||||||
	Type            HookTaskType `xorm:"VARCHAR(16) 'type'"`
 | 
						Type            HookType   `xorm:"VARCHAR(16) 'type'"`
 | 
				
			||||||
	Meta            string     `xorm:"TEXT"` // store hook-specific attributes
 | 
						Meta            string     `xorm:"TEXT"` // store hook-specific attributes
 | 
				
			||||||
	LastStatus      HookStatus // Last delivery status
 | 
						LastStatus      HookStatus // Last delivery status
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -558,22 +572,6 @@ func copyDefaultWebhooksToRepo(e Engine, repoID int64) error {
 | 
				
			||||||
//  \___|_  / \____/ \____/|__|_ \ |____|  (____  /____  >__|_ \
 | 
					//  \___|_  / \____/ \____/|__|_ \ |____|  (____  /____  >__|_ \
 | 
				
			||||||
//        \/                    \/              \/     \/     \/
 | 
					//        \/                    \/              \/     \/     \/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// HookTaskType is the type of an hook task
 | 
					 | 
				
			||||||
type HookTaskType = string
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Types of hook tasks
 | 
					 | 
				
			||||||
const (
 | 
					 | 
				
			||||||
	GITEA    HookTaskType = "gitea"
 | 
					 | 
				
			||||||
	GOGS     HookTaskType = "gogs"
 | 
					 | 
				
			||||||
	SLACK    HookTaskType = "slack"
 | 
					 | 
				
			||||||
	DISCORD  HookTaskType = "discord"
 | 
					 | 
				
			||||||
	DINGTALK HookTaskType = "dingtalk"
 | 
					 | 
				
			||||||
	TELEGRAM HookTaskType = "telegram"
 | 
					 | 
				
			||||||
	MSTEAMS  HookTaskType = "msteams"
 | 
					 | 
				
			||||||
	FEISHU   HookTaskType = "feishu"
 | 
					 | 
				
			||||||
	MATRIX   HookTaskType = "matrix"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// HookEventType is the type of an hook event
 | 
					// HookEventType is the type of an hook event
 | 
				
			||||||
type HookEventType string
 | 
					type HookEventType string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -635,6 +633,8 @@ func (h HookEventType) Event() string {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// HookRequest represents hook task request information.
 | 
					// HookRequest represents hook task request information.
 | 
				
			||||||
type HookRequest struct {
 | 
					type HookRequest struct {
 | 
				
			||||||
 | 
						URL        string            `json:"url"`
 | 
				
			||||||
 | 
						HTTPMethod string            `json:"http_method"`
 | 
				
			||||||
	Headers    map[string]string `json:"headers"`
 | 
						Headers    map[string]string `json:"headers"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -651,15 +651,9 @@ type HookTask struct {
 | 
				
			||||||
	RepoID          int64 `xorm:"INDEX"`
 | 
						RepoID          int64 `xorm:"INDEX"`
 | 
				
			||||||
	HookID          int64
 | 
						HookID          int64
 | 
				
			||||||
	UUID            string
 | 
						UUID            string
 | 
				
			||||||
	Typ             HookTaskType `xorm:"VARCHAR(16) index"`
 | 
					 | 
				
			||||||
	URL             string       `xorm:"TEXT"`
 | 
					 | 
				
			||||||
	Signature       string       `xorm:"TEXT"`
 | 
					 | 
				
			||||||
	api.Payloader   `xorm:"-"`
 | 
						api.Payloader   `xorm:"-"`
 | 
				
			||||||
	PayloadContent  string `xorm:"TEXT"`
 | 
						PayloadContent  string `xorm:"TEXT"`
 | 
				
			||||||
	HTTPMethod      string `xorm:"http_method"`
 | 
					 | 
				
			||||||
	ContentType     HookContentType
 | 
					 | 
				
			||||||
	EventType       HookEventType
 | 
						EventType       HookEventType
 | 
				
			||||||
	IsSSL           bool
 | 
					 | 
				
			||||||
	IsDelivered     bool
 | 
						IsDelivered     bool
 | 
				
			||||||
	Delivered       int64
 | 
						Delivered       int64
 | 
				
			||||||
	DeliveredString string `xorm:"-"`
 | 
						DeliveredString string `xorm:"-"`
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -207,8 +207,6 @@ func TestCreateHookTask(t *testing.T) {
 | 
				
			||||||
	hookTask := &HookTask{
 | 
						hookTask := &HookTask{
 | 
				
			||||||
		RepoID:    3,
 | 
							RepoID:    3,
 | 
				
			||||||
		HookID:    3,
 | 
							HookID:    3,
 | 
				
			||||||
		Typ:       GITEA,
 | 
					 | 
				
			||||||
		URL:       "http://www.example.com/unit_test",
 | 
					 | 
				
			||||||
		Payloader: &api.PushPayload{},
 | 
							Payloader: &api.PushPayload{},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	AssertNotExistsBean(t, hookTask)
 | 
						AssertNotExistsBean(t, hookTask)
 | 
				
			||||||
| 
						 | 
					@ -233,8 +231,6 @@ func TestCleanupHookTaskTable_PerWebhook_DeletesDelivered(t *testing.T) {
 | 
				
			||||||
	hookTask := &HookTask{
 | 
						hookTask := &HookTask{
 | 
				
			||||||
		RepoID:      3,
 | 
							RepoID:      3,
 | 
				
			||||||
		HookID:      3,
 | 
							HookID:      3,
 | 
				
			||||||
		Typ:         GITEA,
 | 
					 | 
				
			||||||
		URL:         "http://www.example.com/unit_test",
 | 
					 | 
				
			||||||
		Payloader:   &api.PushPayload{},
 | 
							Payloader:   &api.PushPayload{},
 | 
				
			||||||
		IsDelivered: true,
 | 
							IsDelivered: true,
 | 
				
			||||||
		Delivered:   time.Now().UnixNano(),
 | 
							Delivered:   time.Now().UnixNano(),
 | 
				
			||||||
| 
						 | 
					@ -252,8 +248,6 @@ func TestCleanupHookTaskTable_PerWebhook_LeavesUndelivered(t *testing.T) {
 | 
				
			||||||
	hookTask := &HookTask{
 | 
						hookTask := &HookTask{
 | 
				
			||||||
		RepoID:      2,
 | 
							RepoID:      2,
 | 
				
			||||||
		HookID:      4,
 | 
							HookID:      4,
 | 
				
			||||||
		Typ:         GITEA,
 | 
					 | 
				
			||||||
		URL:         "http://www.example.com/unit_test",
 | 
					 | 
				
			||||||
		Payloader:   &api.PushPayload{},
 | 
							Payloader:   &api.PushPayload{},
 | 
				
			||||||
		IsDelivered: false,
 | 
							IsDelivered: false,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -270,8 +264,6 @@ func TestCleanupHookTaskTable_PerWebhook_LeavesMostRecentTask(t *testing.T) {
 | 
				
			||||||
	hookTask := &HookTask{
 | 
						hookTask := &HookTask{
 | 
				
			||||||
		RepoID:      2,
 | 
							RepoID:      2,
 | 
				
			||||||
		HookID:      4,
 | 
							HookID:      4,
 | 
				
			||||||
		Typ:         GITEA,
 | 
					 | 
				
			||||||
		URL:         "http://www.example.com/unit_test",
 | 
					 | 
				
			||||||
		Payloader:   &api.PushPayload{},
 | 
							Payloader:   &api.PushPayload{},
 | 
				
			||||||
		IsDelivered: true,
 | 
							IsDelivered: true,
 | 
				
			||||||
		Delivered:   time.Now().UnixNano(),
 | 
							Delivered:   time.Now().UnixNano(),
 | 
				
			||||||
| 
						 | 
					@ -289,8 +281,6 @@ func TestCleanupHookTaskTable_OlderThan_DeletesDelivered(t *testing.T) {
 | 
				
			||||||
	hookTask := &HookTask{
 | 
						hookTask := &HookTask{
 | 
				
			||||||
		RepoID:      3,
 | 
							RepoID:      3,
 | 
				
			||||||
		HookID:      3,
 | 
							HookID:      3,
 | 
				
			||||||
		Typ:         GITEA,
 | 
					 | 
				
			||||||
		URL:         "http://www.example.com/unit_test",
 | 
					 | 
				
			||||||
		Payloader:   &api.PushPayload{},
 | 
							Payloader:   &api.PushPayload{},
 | 
				
			||||||
		IsDelivered: true,
 | 
							IsDelivered: true,
 | 
				
			||||||
		Delivered:   time.Now().AddDate(0, 0, -8).UnixNano(),
 | 
							Delivered:   time.Now().AddDate(0, 0, -8).UnixNano(),
 | 
				
			||||||
| 
						 | 
					@ -308,8 +298,6 @@ func TestCleanupHookTaskTable_OlderThan_LeavesUndelivered(t *testing.T) {
 | 
				
			||||||
	hookTask := &HookTask{
 | 
						hookTask := &HookTask{
 | 
				
			||||||
		RepoID:      2,
 | 
							RepoID:      2,
 | 
				
			||||||
		HookID:      4,
 | 
							HookID:      4,
 | 
				
			||||||
		Typ:         GITEA,
 | 
					 | 
				
			||||||
		URL:         "http://www.example.com/unit_test",
 | 
					 | 
				
			||||||
		Payloader:   &api.PushPayload{},
 | 
							Payloader:   &api.PushPayload{},
 | 
				
			||||||
		IsDelivered: false,
 | 
							IsDelivered: false,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -326,8 +314,6 @@ func TestCleanupHookTaskTable_OlderThan_LeavesTaskEarlierThanAgeToDelete(t *test
 | 
				
			||||||
	hookTask := &HookTask{
 | 
						hookTask := &HookTask{
 | 
				
			||||||
		RepoID:      2,
 | 
							RepoID:      2,
 | 
				
			||||||
		HookID:      4,
 | 
							HookID:      4,
 | 
				
			||||||
		Typ:         GITEA,
 | 
					 | 
				
			||||||
		URL:         "http://www.example.com/unit_test",
 | 
					 | 
				
			||||||
		Payloader:   &api.PushPayload{},
 | 
							Payloader:   &api.PushPayload{},
 | 
				
			||||||
		IsDelivered: true,
 | 
							IsDelivered: true,
 | 
				
			||||||
		Delivered:   time.Now().AddDate(0, 0, -6).UnixNano(),
 | 
							Delivered:   time.Now().AddDate(0, 0, -6).UnixNano(),
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -62,7 +62,6 @@ type EditHookOption struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Payloader payload is some part of one hook
 | 
					// Payloader payload is some part of one hook
 | 
				
			||||||
type Payloader interface {
 | 
					type Payloader interface {
 | 
				
			||||||
	SetSecret(string)
 | 
					 | 
				
			||||||
	JSONPayload() ([]byte, error)
 | 
						JSONPayload() ([]byte, error)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -124,7 +123,6 @@ var (
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CreatePayload FIXME
 | 
					// CreatePayload FIXME
 | 
				
			||||||
type CreatePayload struct {
 | 
					type CreatePayload struct {
 | 
				
			||||||
	Secret  string      `json:"secret"`
 | 
					 | 
				
			||||||
	Sha     string      `json:"sha"`
 | 
						Sha     string      `json:"sha"`
 | 
				
			||||||
	Ref     string      `json:"ref"`
 | 
						Ref     string      `json:"ref"`
 | 
				
			||||||
	RefType string      `json:"ref_type"`
 | 
						RefType string      `json:"ref_type"`
 | 
				
			||||||
| 
						 | 
					@ -132,11 +130,6 @@ type CreatePayload struct {
 | 
				
			||||||
	Sender  *User       `json:"sender"`
 | 
						Sender  *User       `json:"sender"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SetSecret modifies the secret of the CreatePayload
 | 
					 | 
				
			||||||
func (p *CreatePayload) SetSecret(secret string) {
 | 
					 | 
				
			||||||
	p.Secret = secret
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// JSONPayload return payload information
 | 
					// JSONPayload return payload information
 | 
				
			||||||
func (p *CreatePayload) JSONPayload() ([]byte, error) {
 | 
					func (p *CreatePayload) JSONPayload() ([]byte, error) {
 | 
				
			||||||
	json := jsoniter.ConfigCompatibleWithStandardLibrary
 | 
						json := jsoniter.ConfigCompatibleWithStandardLibrary
 | 
				
			||||||
| 
						 | 
					@ -181,7 +174,6 @@ const (
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// DeletePayload represents delete payload
 | 
					// DeletePayload represents delete payload
 | 
				
			||||||
type DeletePayload struct {
 | 
					type DeletePayload struct {
 | 
				
			||||||
	Secret     string      `json:"secret"`
 | 
					 | 
				
			||||||
	Ref        string      `json:"ref"`
 | 
						Ref        string      `json:"ref"`
 | 
				
			||||||
	RefType    string      `json:"ref_type"`
 | 
						RefType    string      `json:"ref_type"`
 | 
				
			||||||
	PusherType PusherType  `json:"pusher_type"`
 | 
						PusherType PusherType  `json:"pusher_type"`
 | 
				
			||||||
| 
						 | 
					@ -189,11 +181,6 @@ type DeletePayload struct {
 | 
				
			||||||
	Sender     *User       `json:"sender"`
 | 
						Sender     *User       `json:"sender"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SetSecret modifies the secret of the DeletePayload
 | 
					 | 
				
			||||||
func (p *DeletePayload) SetSecret(secret string) {
 | 
					 | 
				
			||||||
	p.Secret = secret
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// JSONPayload implements Payload
 | 
					// JSONPayload implements Payload
 | 
				
			||||||
func (p *DeletePayload) JSONPayload() ([]byte, error) {
 | 
					func (p *DeletePayload) JSONPayload() ([]byte, error) {
 | 
				
			||||||
	json := jsoniter.ConfigCompatibleWithStandardLibrary
 | 
						json := jsoniter.ConfigCompatibleWithStandardLibrary
 | 
				
			||||||
| 
						 | 
					@ -209,17 +196,11 @@ func (p *DeletePayload) JSONPayload() ([]byte, error) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ForkPayload represents fork payload
 | 
					// ForkPayload represents fork payload
 | 
				
			||||||
type ForkPayload struct {
 | 
					type ForkPayload struct {
 | 
				
			||||||
	Secret string      `json:"secret"`
 | 
					 | 
				
			||||||
	Forkee *Repository `json:"forkee"`
 | 
						Forkee *Repository `json:"forkee"`
 | 
				
			||||||
	Repo   *Repository `json:"repository"`
 | 
						Repo   *Repository `json:"repository"`
 | 
				
			||||||
	Sender *User       `json:"sender"`
 | 
						Sender *User       `json:"sender"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SetSecret modifies the secret of the ForkPayload
 | 
					 | 
				
			||||||
func (p *ForkPayload) SetSecret(secret string) {
 | 
					 | 
				
			||||||
	p.Secret = secret
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// JSONPayload implements Payload
 | 
					// JSONPayload implements Payload
 | 
				
			||||||
func (p *ForkPayload) JSONPayload() ([]byte, error) {
 | 
					func (p *ForkPayload) JSONPayload() ([]byte, error) {
 | 
				
			||||||
	json := jsoniter.ConfigCompatibleWithStandardLibrary
 | 
						json := jsoniter.ConfigCompatibleWithStandardLibrary
 | 
				
			||||||
| 
						 | 
					@ -238,7 +219,6 @@ const (
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// IssueCommentPayload represents a payload information of issue comment event.
 | 
					// IssueCommentPayload represents a payload information of issue comment event.
 | 
				
			||||||
type IssueCommentPayload struct {
 | 
					type IssueCommentPayload struct {
 | 
				
			||||||
	Secret     string                 `json:"secret"`
 | 
					 | 
				
			||||||
	Action     HookIssueCommentAction `json:"action"`
 | 
						Action     HookIssueCommentAction `json:"action"`
 | 
				
			||||||
	Issue      *Issue                 `json:"issue"`
 | 
						Issue      *Issue                 `json:"issue"`
 | 
				
			||||||
	Comment    *Comment               `json:"comment"`
 | 
						Comment    *Comment               `json:"comment"`
 | 
				
			||||||
| 
						 | 
					@ -248,11 +228,6 @@ type IssueCommentPayload struct {
 | 
				
			||||||
	IsPull     bool                   `json:"is_pull"`
 | 
						IsPull     bool                   `json:"is_pull"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SetSecret modifies the secret of the IssueCommentPayload
 | 
					 | 
				
			||||||
func (p *IssueCommentPayload) SetSecret(secret string) {
 | 
					 | 
				
			||||||
	p.Secret = secret
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// JSONPayload implements Payload
 | 
					// JSONPayload implements Payload
 | 
				
			||||||
func (p *IssueCommentPayload) JSONPayload() ([]byte, error) {
 | 
					func (p *IssueCommentPayload) JSONPayload() ([]byte, error) {
 | 
				
			||||||
	json := jsoniter.ConfigCompatibleWithStandardLibrary
 | 
						json := jsoniter.ConfigCompatibleWithStandardLibrary
 | 
				
			||||||
| 
						 | 
					@ -278,18 +253,12 @@ const (
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ReleasePayload represents a payload information of release event.
 | 
					// ReleasePayload represents a payload information of release event.
 | 
				
			||||||
type ReleasePayload struct {
 | 
					type ReleasePayload struct {
 | 
				
			||||||
	Secret     string            `json:"secret"`
 | 
					 | 
				
			||||||
	Action     HookReleaseAction `json:"action"`
 | 
						Action     HookReleaseAction `json:"action"`
 | 
				
			||||||
	Release    *Release          `json:"release"`
 | 
						Release    *Release          `json:"release"`
 | 
				
			||||||
	Repository *Repository       `json:"repository"`
 | 
						Repository *Repository       `json:"repository"`
 | 
				
			||||||
	Sender     *User             `json:"sender"`
 | 
						Sender     *User             `json:"sender"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SetSecret modifies the secret of the ReleasePayload
 | 
					 | 
				
			||||||
func (p *ReleasePayload) SetSecret(secret string) {
 | 
					 | 
				
			||||||
	p.Secret = secret
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// JSONPayload implements Payload
 | 
					// JSONPayload implements Payload
 | 
				
			||||||
func (p *ReleasePayload) JSONPayload() ([]byte, error) {
 | 
					func (p *ReleasePayload) JSONPayload() ([]byte, error) {
 | 
				
			||||||
	json := jsoniter.ConfigCompatibleWithStandardLibrary
 | 
						json := jsoniter.ConfigCompatibleWithStandardLibrary
 | 
				
			||||||
| 
						 | 
					@ -305,7 +274,6 @@ func (p *ReleasePayload) JSONPayload() ([]byte, error) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// PushPayload represents a payload information of push event.
 | 
					// PushPayload represents a payload information of push event.
 | 
				
			||||||
type PushPayload struct {
 | 
					type PushPayload struct {
 | 
				
			||||||
	Secret     string           `json:"secret"`
 | 
					 | 
				
			||||||
	Ref        string           `json:"ref"`
 | 
						Ref        string           `json:"ref"`
 | 
				
			||||||
	Before     string           `json:"before"`
 | 
						Before     string           `json:"before"`
 | 
				
			||||||
	After      string           `json:"after"`
 | 
						After      string           `json:"after"`
 | 
				
			||||||
| 
						 | 
					@ -317,11 +285,6 @@ type PushPayload struct {
 | 
				
			||||||
	Sender     *User            `json:"sender"`
 | 
						Sender     *User            `json:"sender"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SetSecret modifies the secret of the PushPayload
 | 
					 | 
				
			||||||
func (p *PushPayload) SetSecret(secret string) {
 | 
					 | 
				
			||||||
	p.Secret = secret
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// JSONPayload FIXME
 | 
					// JSONPayload FIXME
 | 
				
			||||||
func (p *PushPayload) JSONPayload() ([]byte, error) {
 | 
					func (p *PushPayload) JSONPayload() ([]byte, error) {
 | 
				
			||||||
	json := jsoniter.ConfigCompatibleWithStandardLibrary
 | 
						json := jsoniter.ConfigCompatibleWithStandardLibrary
 | 
				
			||||||
| 
						 | 
					@ -389,7 +352,6 @@ const (
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// IssuePayload represents the payload information that is sent along with an issue event.
 | 
					// IssuePayload represents the payload information that is sent along with an issue event.
 | 
				
			||||||
type IssuePayload struct {
 | 
					type IssuePayload struct {
 | 
				
			||||||
	Secret     string          `json:"secret"`
 | 
					 | 
				
			||||||
	Action     HookIssueAction `json:"action"`
 | 
						Action     HookIssueAction `json:"action"`
 | 
				
			||||||
	Index      int64           `json:"number"`
 | 
						Index      int64           `json:"number"`
 | 
				
			||||||
	Changes    *ChangesPayload `json:"changes,omitempty"`
 | 
						Changes    *ChangesPayload `json:"changes,omitempty"`
 | 
				
			||||||
| 
						 | 
					@ -398,11 +360,6 @@ type IssuePayload struct {
 | 
				
			||||||
	Sender     *User           `json:"sender"`
 | 
						Sender     *User           `json:"sender"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SetSecret modifies the secret of the IssuePayload.
 | 
					 | 
				
			||||||
func (p *IssuePayload) SetSecret(secret string) {
 | 
					 | 
				
			||||||
	p.Secret = secret
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// JSONPayload encodes the IssuePayload to JSON, with an indentation of two spaces.
 | 
					// JSONPayload encodes the IssuePayload to JSON, with an indentation of two spaces.
 | 
				
			||||||
func (p *IssuePayload) JSONPayload() ([]byte, error) {
 | 
					func (p *IssuePayload) JSONPayload() ([]byte, error) {
 | 
				
			||||||
	json := jsoniter.ConfigCompatibleWithStandardLibrary
 | 
						json := jsoniter.ConfigCompatibleWithStandardLibrary
 | 
				
			||||||
| 
						 | 
					@ -430,7 +387,6 @@ type ChangesPayload struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// PullRequestPayload represents a payload information of pull request event.
 | 
					// PullRequestPayload represents a payload information of pull request event.
 | 
				
			||||||
type PullRequestPayload struct {
 | 
					type PullRequestPayload struct {
 | 
				
			||||||
	Secret      string          `json:"secret"`
 | 
					 | 
				
			||||||
	Action      HookIssueAction `json:"action"`
 | 
						Action      HookIssueAction `json:"action"`
 | 
				
			||||||
	Index       int64           `json:"number"`
 | 
						Index       int64           `json:"number"`
 | 
				
			||||||
	Changes     *ChangesPayload `json:"changes,omitempty"`
 | 
						Changes     *ChangesPayload `json:"changes,omitempty"`
 | 
				
			||||||
| 
						 | 
					@ -440,11 +396,6 @@ type PullRequestPayload struct {
 | 
				
			||||||
	Review      *ReviewPayload  `json:"review"`
 | 
						Review      *ReviewPayload  `json:"review"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SetSecret modifies the secret of the PullRequestPayload.
 | 
					 | 
				
			||||||
func (p *PullRequestPayload) SetSecret(secret string) {
 | 
					 | 
				
			||||||
	p.Secret = secret
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// JSONPayload FIXME
 | 
					// JSONPayload FIXME
 | 
				
			||||||
func (p *PullRequestPayload) JSONPayload() ([]byte, error) {
 | 
					func (p *PullRequestPayload) JSONPayload() ([]byte, error) {
 | 
				
			||||||
	json := jsoniter.ConfigCompatibleWithStandardLibrary
 | 
						json := jsoniter.ConfigCompatibleWithStandardLibrary
 | 
				
			||||||
| 
						 | 
					@ -476,18 +427,12 @@ const (
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RepositoryPayload payload for repository webhooks
 | 
					// RepositoryPayload payload for repository webhooks
 | 
				
			||||||
type RepositoryPayload struct {
 | 
					type RepositoryPayload struct {
 | 
				
			||||||
	Secret       string         `json:"secret"`
 | 
					 | 
				
			||||||
	Action       HookRepoAction `json:"action"`
 | 
						Action       HookRepoAction `json:"action"`
 | 
				
			||||||
	Repository   *Repository    `json:"repository"`
 | 
						Repository   *Repository    `json:"repository"`
 | 
				
			||||||
	Organization *User          `json:"organization"`
 | 
						Organization *User          `json:"organization"`
 | 
				
			||||||
	Sender       *User          `json:"sender"`
 | 
						Sender       *User          `json:"sender"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SetSecret modifies the secret of the RepositoryPayload
 | 
					 | 
				
			||||||
func (p *RepositoryPayload) SetSecret(secret string) {
 | 
					 | 
				
			||||||
	p.Secret = secret
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// JSONPayload JSON representation of the payload
 | 
					// JSONPayload JSON representation of the payload
 | 
				
			||||||
func (p *RepositoryPayload) JSONPayload() ([]byte, error) {
 | 
					func (p *RepositoryPayload) JSONPayload() ([]byte, error) {
 | 
				
			||||||
	json := jsoniter.ConfigCompatibleWithStandardLibrary
 | 
						json := jsoniter.ConfigCompatibleWithStandardLibrary
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -133,7 +133,7 @@ func addHook(ctx *context.APIContext, form *api.CreateHookOption, orgID, repoID
 | 
				
			||||||
			BranchFilter: form.BranchFilter,
 | 
								BranchFilter: form.BranchFilter,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		IsActive: form.Active,
 | 
							IsActive: form.Active,
 | 
				
			||||||
		Type:     models.HookTaskType(form.Type),
 | 
							Type:     models.HookType(form.Type),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if w.Type == models.SLACK {
 | 
						if w.Type == models.SLACK {
 | 
				
			||||||
		channel, ok := form.Config["channel"]
 | 
							channel, ok := form.Config["channel"]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -239,7 +239,7 @@ func GogsHooksNewPost(ctx *context.Context) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// newGogsWebhookPost response for creating gogs hook
 | 
					// newGogsWebhookPost response for creating gogs hook
 | 
				
			||||||
func newGogsWebhookPost(ctx *context.Context, form forms.NewGogshookForm, kind models.HookTaskType) {
 | 
					func newGogsWebhookPost(ctx *context.Context, form forms.NewGogshookForm, kind models.HookType) {
 | 
				
			||||||
	ctx.Data["Title"] = ctx.Tr("repo.settings.add_webhook")
 | 
						ctx.Data["Title"] = ctx.Tr("repo.settings.add_webhook")
 | 
				
			||||||
	ctx.Data["PageIsSettingsHooks"] = true
 | 
						ctx.Data["PageIsSettingsHooks"] = true
 | 
				
			||||||
	ctx.Data["PageIsSettingsHooksNew"] = true
 | 
						ctx.Data["PageIsSettingsHooksNew"] = true
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,8 +6,13 @@ package webhook
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
 | 
						"crypto/hmac"
 | 
				
			||||||
 | 
						"crypto/sha1"
 | 
				
			||||||
 | 
						"crypto/sha256"
 | 
				
			||||||
	"crypto/tls"
 | 
						"crypto/tls"
 | 
				
			||||||
 | 
						"encoding/hex"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
	"io/ioutil"
 | 
						"io/ioutil"
 | 
				
			||||||
	"net"
 | 
						"net"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
| 
						 | 
					@ -26,27 +31,32 @@ import (
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Deliver deliver hook task
 | 
					// Deliver deliver hook task
 | 
				
			||||||
func Deliver(t *models.HookTask) error {
 | 
					func Deliver(t *models.HookTask) error {
 | 
				
			||||||
 | 
						w, err := models.GetWebhookByID(t.HookID)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	defer func() {
 | 
						defer func() {
 | 
				
			||||||
		err := recover()
 | 
							err := recover()
 | 
				
			||||||
		if err == nil {
 | 
							if err == nil {
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		// There was a panic whilst delivering a hook...
 | 
							// There was a panic whilst delivering a hook...
 | 
				
			||||||
		log.Error("PANIC whilst trying to deliver webhook[%d] for repo[%d] to %s Panic: %v\nStacktrace: %s", t.ID, t.RepoID, t.URL, err, log.Stack(2))
 | 
							log.Error("PANIC whilst trying to deliver webhook[%d] for repo[%d] to %s Panic: %v\nStacktrace: %s", t.ID, t.RepoID, w.URL, err, log.Stack(2))
 | 
				
			||||||
	}()
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	t.IsDelivered = true
 | 
						t.IsDelivered = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var req *http.Request
 | 
						var req *http.Request
 | 
				
			||||||
	var err error
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch t.HTTPMethod {
 | 
						switch w.HTTPMethod {
 | 
				
			||||||
	case "":
 | 
						case "":
 | 
				
			||||||
		log.Info("HTTP Method for webhook %d empty, setting to POST as default", t.ID)
 | 
							log.Info("HTTP Method for webhook %d empty, setting to POST as default", t.ID)
 | 
				
			||||||
		fallthrough
 | 
							fallthrough
 | 
				
			||||||
	case http.MethodPost:
 | 
						case http.MethodPost:
 | 
				
			||||||
		switch t.ContentType {
 | 
							switch w.ContentType {
 | 
				
			||||||
		case models.ContentTypeJSON:
 | 
							case models.ContentTypeJSON:
 | 
				
			||||||
			req, err = http.NewRequest("POST", t.URL, strings.NewReader(t.PayloadContent))
 | 
								req, err = http.NewRequest("POST", w.URL, strings.NewReader(t.PayloadContent))
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				return err
 | 
									return err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
| 
						 | 
					@ -57,16 +67,15 @@ func Deliver(t *models.HookTask) error {
 | 
				
			||||||
				"payload": []string{t.PayloadContent},
 | 
									"payload": []string{t.PayloadContent},
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			req, err = http.NewRequest("POST", t.URL, strings.NewReader(forms.Encode()))
 | 
								req, err = http.NewRequest("POST", w.URL, strings.NewReader(forms.Encode()))
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
 | 
					 | 
				
			||||||
				return err
 | 
									return err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
 | 
								req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	case http.MethodGet:
 | 
						case http.MethodGet:
 | 
				
			||||||
		u, err := url.Parse(t.URL)
 | 
							u, err := url.Parse(w.URL)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -78,30 +87,47 @@ func Deliver(t *models.HookTask) error {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	case http.MethodPut:
 | 
						case http.MethodPut:
 | 
				
			||||||
		switch t.Typ {
 | 
							switch w.Type {
 | 
				
			||||||
		case models.MATRIX:
 | 
							case models.MATRIX:
 | 
				
			||||||
			req, err = getMatrixHookRequest(t)
 | 
								req, err = getMatrixHookRequest(w, t)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				return err
 | 
									return err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
			return fmt.Errorf("Invalid http method for webhook: [%d] %v", t.ID, t.HTTPMethod)
 | 
								return fmt.Errorf("Invalid http method for webhook: [%d] %v", t.ID, w.HTTPMethod)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		return fmt.Errorf("Invalid http method for webhook: [%d] %v", t.ID, t.HTTPMethod)
 | 
							return fmt.Errorf("Invalid http method for webhook: [%d] %v", t.ID, w.HTTPMethod)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var signatureSHA1 string
 | 
				
			||||||
 | 
						var signatureSHA256 string
 | 
				
			||||||
 | 
						if len(w.Secret) > 0 {
 | 
				
			||||||
 | 
							sig1 := hmac.New(sha1.New, []byte(w.Secret))
 | 
				
			||||||
 | 
							sig256 := hmac.New(sha256.New, []byte(w.Secret))
 | 
				
			||||||
 | 
							_, err = io.MultiWriter(sig1, sig256).Write([]byte(t.PayloadContent))
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								log.Error("prepareWebhooks.sigWrite: %v", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							signatureSHA1 = hex.EncodeToString(sig1.Sum(nil))
 | 
				
			||||||
 | 
							signatureSHA256 = hex.EncodeToString(sig256.Sum(nil))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	req.Header.Add("X-Gitea-Delivery", t.UUID)
 | 
						req.Header.Add("X-Gitea-Delivery", t.UUID)
 | 
				
			||||||
	req.Header.Add("X-Gitea-Event", t.EventType.Event())
 | 
						req.Header.Add("X-Gitea-Event", t.EventType.Event())
 | 
				
			||||||
	req.Header.Add("X-Gitea-Signature", t.Signature)
 | 
						req.Header.Add("X-Gitea-Signature", signatureSHA256)
 | 
				
			||||||
	req.Header.Add("X-Gogs-Delivery", t.UUID)
 | 
						req.Header.Add("X-Gogs-Delivery", t.UUID)
 | 
				
			||||||
	req.Header.Add("X-Gogs-Event", t.EventType.Event())
 | 
						req.Header.Add("X-Gogs-Event", t.EventType.Event())
 | 
				
			||||||
	req.Header.Add("X-Gogs-Signature", t.Signature)
 | 
						req.Header.Add("X-Gogs-Signature", signatureSHA256)
 | 
				
			||||||
 | 
						req.Header.Add("X-Hub-Signature", "sha1="+signatureSHA1)
 | 
				
			||||||
 | 
						req.Header.Add("X-Hub-Signature-256", "sha256="+signatureSHA256)
 | 
				
			||||||
	req.Header["X-GitHub-Delivery"] = []string{t.UUID}
 | 
						req.Header["X-GitHub-Delivery"] = []string{t.UUID}
 | 
				
			||||||
	req.Header["X-GitHub-Event"] = []string{t.EventType.Event()}
 | 
						req.Header["X-GitHub-Event"] = []string{t.EventType.Event()}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Record delivery information.
 | 
						// Record delivery information.
 | 
				
			||||||
	t.RequestInfo = &models.HookRequest{
 | 
						t.RequestInfo = &models.HookRequest{
 | 
				
			||||||
 | 
							URL:        req.URL.String(),
 | 
				
			||||||
 | 
							HTTPMethod: req.Method,
 | 
				
			||||||
		Headers:    map[string]string{},
 | 
							Headers:    map[string]string{},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for k, vals := range req.Header {
 | 
						for k, vals := range req.Header {
 | 
				
			||||||
| 
						 | 
					@ -125,11 +151,6 @@ func Deliver(t *models.HookTask) error {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Update webhook last delivery status.
 | 
							// Update webhook last delivery status.
 | 
				
			||||||
		w, err := models.GetWebhookByID(t.HookID)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			log.Error("GetWebhookByID: %v", err)
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if t.IsSucceed {
 | 
							if t.IsSucceed {
 | 
				
			||||||
			w.LastStatus = models.HookStatusSucceed
 | 
								w.LastStatus = models.HookStatusSucceed
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,9 +25,6 @@ var (
 | 
				
			||||||
	_ PayloadConvertor = &DingtalkPayload{}
 | 
						_ PayloadConvertor = &DingtalkPayload{}
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SetSecret sets the dingtalk secret
 | 
					 | 
				
			||||||
func (d *DingtalkPayload) SetSecret(_ string) {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// JSONPayload Marshals the DingtalkPayload to json
 | 
					// JSONPayload Marshals the DingtalkPayload to json
 | 
				
			||||||
func (d *DingtalkPayload) JSONPayload() ([]byte, error) {
 | 
					func (d *DingtalkPayload) JSONPayload() ([]byte, error) {
 | 
				
			||||||
	json := jsoniter.ConfigCompatibleWithStandardLibrary
 | 
						json := jsoniter.ConfigCompatibleWithStandardLibrary
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -97,9 +97,6 @@ var (
 | 
				
			||||||
	redColor         = color("ff3232")
 | 
						redColor         = color("ff3232")
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SetSecret sets the discord secret
 | 
					 | 
				
			||||||
func (d *DiscordPayload) SetSecret(_ string) {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// JSONPayload Marshals the DiscordPayload to json
 | 
					// JSONPayload Marshals the DiscordPayload to json
 | 
				
			||||||
func (d *DiscordPayload) JSONPayload() ([]byte, error) {
 | 
					func (d *DiscordPayload) JSONPayload() ([]byte, error) {
 | 
				
			||||||
	json := jsoniter.ConfigCompatibleWithStandardLibrary
 | 
						json := jsoniter.ConfigCompatibleWithStandardLibrary
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -35,9 +35,6 @@ func newFeishuTextPayload(text string) *FeishuPayload {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SetSecret sets the Feishu secret
 | 
					 | 
				
			||||||
func (f *FeishuPayload) SetSecret(_ string) {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// JSONPayload Marshals the FeishuPayload to json
 | 
					// JSONPayload Marshals the FeishuPayload to json
 | 
				
			||||||
func (f *FeishuPayload) JSONPayload() ([]byte, error) {
 | 
					func (f *FeishuPayload) JSONPayload() ([]byte, error) {
 | 
				
			||||||
	json := jsoniter.ConfigCompatibleWithStandardLibrary
 | 
						json := jsoniter.ConfigCompatibleWithStandardLibrary
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -76,9 +76,6 @@ type MatrixPayloadSafe struct {
 | 
				
			||||||
	Commits       []*api.PayloadCommit `json:"io.gitea.commits,omitempty"`
 | 
						Commits       []*api.PayloadCommit `json:"io.gitea.commits,omitempty"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SetSecret sets the Matrix secret
 | 
					 | 
				
			||||||
func (m *MatrixPayloadUnsafe) SetSecret(_ string) {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// JSONPayload Marshals the MatrixPayloadUnsafe to json
 | 
					// JSONPayload Marshals the MatrixPayloadUnsafe to json
 | 
				
			||||||
func (m *MatrixPayloadUnsafe) JSONPayload() ([]byte, error) {
 | 
					func (m *MatrixPayloadUnsafe) JSONPayload() ([]byte, error) {
 | 
				
			||||||
	json := jsoniter.ConfigCompatibleWithStandardLibrary
 | 
						json := jsoniter.ConfigCompatibleWithStandardLibrary
 | 
				
			||||||
| 
						 | 
					@ -263,7 +260,7 @@ func getMessageBody(htmlText string) string {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// getMatrixHookRequest creates a new request which contains an Authorization header.
 | 
					// getMatrixHookRequest creates a new request which contains an Authorization header.
 | 
				
			||||||
// The access_token is removed from t.PayloadContent
 | 
					// The access_token is removed from t.PayloadContent
 | 
				
			||||||
func getMatrixHookRequest(t *models.HookTask) (*http.Request, error) {
 | 
					func getMatrixHookRequest(w *models.Webhook, t *models.HookTask) (*http.Request, error) {
 | 
				
			||||||
	payloadunsafe := MatrixPayloadUnsafe{}
 | 
						payloadunsafe := MatrixPayloadUnsafe{}
 | 
				
			||||||
	json := jsoniter.ConfigCompatibleWithStandardLibrary
 | 
						json := jsoniter.ConfigCompatibleWithStandardLibrary
 | 
				
			||||||
	if err := json.Unmarshal([]byte(t.PayloadContent), &payloadunsafe); err != nil {
 | 
						if err := json.Unmarshal([]byte(t.PayloadContent), &payloadunsafe); err != nil {
 | 
				
			||||||
| 
						 | 
					@ -288,9 +285,9 @@ func getMatrixHookRequest(t *models.HookTask) (*http.Request, error) {
 | 
				
			||||||
		return nil, fmt.Errorf("getMatrixHookRequest: unable to hash payload: %+v", err)
 | 
							return nil, fmt.Errorf("getMatrixHookRequest: unable to hash payload: %+v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	t.URL = fmt.Sprintf("%s/%s", t.URL, txnID)
 | 
						url := fmt.Sprintf("%s/%s", w.URL, txnID)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	req, err := http.NewRequest(t.HTTPMethod, t.URL, strings.NewReader(string(payload)))
 | 
						req, err := http.NewRequest(w.HTTPMethod, url, strings.NewReader(string(payload)))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -184,6 +184,8 @@ func TestMatrixJSONPayload(t *testing.T) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestMatrixHookRequest(t *testing.T) {
 | 
					func TestMatrixHookRequest(t *testing.T) {
 | 
				
			||||||
 | 
						w := &models.Webhook{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	h := &models.HookTask{
 | 
						h := &models.HookTask{
 | 
				
			||||||
		PayloadContent: `{
 | 
							PayloadContent: `{
 | 
				
			||||||
  "body": "[[user1/test](http://localhost:3000/user1/test)] user1 pushed 1 commit to [master](http://localhost:3000/user1/test/src/branch/master):\n[5175ef2](http://localhost:3000/user1/test/commit/5175ef26201c58b035a3404b3fe02b4e8d436eee): Merge pull request 'Change readme.md' (#2) from add-matrix-webhook into master\n\nReviewed-on: http://localhost:3000/user1/test/pulls/2\n - user1",
 | 
					  "body": "[[user1/test](http://localhost:3000/user1/test)] user1 pushed 1 commit to [master](http://localhost:3000/user1/test/src/branch/master):\n[5175ef2](http://localhost:3000/user1/test/commit/5175ef26201c58b035a3404b3fe02b4e8d436eee): Merge pull request 'Change readme.md' (#2) from add-matrix-webhook into master\n\nReviewed-on: http://localhost:3000/user1/test/pulls/2\n - user1",
 | 
				
			||||||
| 
						 | 
					@ -245,7 +247,7 @@ func TestMatrixHookRequest(t *testing.T) {
 | 
				
			||||||
  ]
 | 
					  ]
 | 
				
			||||||
}`
 | 
					}`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	req, err := getMatrixHookRequest(h)
 | 
						req, err := getMatrixHookRequest(w, h)
 | 
				
			||||||
	require.NoError(t, err)
 | 
						require.NoError(t, err)
 | 
				
			||||||
	require.NotNil(t, req)
 | 
						require.NotNil(t, req)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -55,9 +55,6 @@ type (
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SetSecret sets the MSTeams secret
 | 
					 | 
				
			||||||
func (m *MSTeamsPayload) SetSecret(_ string) {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// JSONPayload Marshals the MSTeamsPayload to json
 | 
					// JSONPayload Marshals the MSTeamsPayload to json
 | 
				
			||||||
func (m *MSTeamsPayload) JSONPayload() ([]byte, error) {
 | 
					func (m *MSTeamsPayload) JSONPayload() ([]byte, error) {
 | 
				
			||||||
	json := jsoniter.ConfigCompatibleWithStandardLibrary
 | 
						json := jsoniter.ConfigCompatibleWithStandardLibrary
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -56,9 +56,6 @@ type SlackAttachment struct {
 | 
				
			||||||
	Text      string `json:"text"`
 | 
						Text      string `json:"text"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SetSecret sets the slack secret
 | 
					 | 
				
			||||||
func (s *SlackPayload) SetSecret(_ string) {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// JSONPayload Marshals the SlackPayload to json
 | 
					// JSONPayload Marshals the SlackPayload to json
 | 
				
			||||||
func (s *SlackPayload) JSONPayload() ([]byte, error) {
 | 
					func (s *SlackPayload) JSONPayload() ([]byte, error) {
 | 
				
			||||||
	json := jsoniter.ConfigCompatibleWithStandardLibrary
 | 
						json := jsoniter.ConfigCompatibleWithStandardLibrary
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -45,9 +45,6 @@ var (
 | 
				
			||||||
	_ PayloadConvertor = &TelegramPayload{}
 | 
						_ PayloadConvertor = &TelegramPayload{}
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SetSecret sets the telegram secret
 | 
					 | 
				
			||||||
func (t *TelegramPayload) SetSecret(_ string) {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// JSONPayload Marshals the TelegramPayload to json
 | 
					// JSONPayload Marshals the TelegramPayload to json
 | 
				
			||||||
func (t *TelegramPayload) JSONPayload() ([]byte, error) {
 | 
					func (t *TelegramPayload) JSONPayload() ([]byte, error) {
 | 
				
			||||||
	t.ParseMode = "HTML"
 | 
						t.ParseMode = "HTML"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,9 +5,6 @@
 | 
				
			||||||
package webhook
 | 
					package webhook
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"crypto/hmac"
 | 
					 | 
				
			||||||
	"crypto/sha256"
 | 
					 | 
				
			||||||
	"encoding/hex"
 | 
					 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,12 +18,12 @@ import (
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type webhook struct {
 | 
					type webhook struct {
 | 
				
			||||||
	name           models.HookTaskType
 | 
						name           models.HookType
 | 
				
			||||||
	payloadCreator func(p api.Payloader, event models.HookEventType, meta string) (api.Payloader, error)
 | 
						payloadCreator func(p api.Payloader, event models.HookEventType, meta string) (api.Payloader, error)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
	webhooks = map[models.HookTaskType]*webhook{
 | 
						webhooks = map[models.HookType]*webhook{
 | 
				
			||||||
		models.SLACK: {
 | 
							models.SLACK: {
 | 
				
			||||||
			name:           models.SLACK,
 | 
								name:           models.SLACK,
 | 
				
			||||||
			payloadCreator: GetSlackPayload,
 | 
								payloadCreator: GetSlackPayload,
 | 
				
			||||||
| 
						 | 
					@ -60,7 +57,7 @@ var (
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RegisterWebhook registers a webhook
 | 
					// RegisterWebhook registers a webhook
 | 
				
			||||||
func RegisterWebhook(name string, webhook *webhook) {
 | 
					func RegisterWebhook(name string, webhook *webhook) {
 | 
				
			||||||
	webhooks[models.HookTaskType(name)] = webhook
 | 
						webhooks[models.HookType(name)] = webhook
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// IsValidHookTaskType returns true if a webhook registered
 | 
					// IsValidHookTaskType returns true if a webhook registered
 | 
				
			||||||
| 
						 | 
					@ -68,7 +65,7 @@ func IsValidHookTaskType(name string) bool {
 | 
				
			||||||
	if name == models.GITEA || name == models.GOGS {
 | 
						if name == models.GITEA || name == models.GOGS {
 | 
				
			||||||
		return true
 | 
							return true
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	_, ok := webhooks[models.HookTaskType(name)]
 | 
						_, ok := webhooks[models.HookType(name)]
 | 
				
			||||||
	return ok
 | 
						return ok
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -161,35 +158,14 @@ func prepareWebhook(w *models.Webhook, repo *models.Repository, event models.Hoo
 | 
				
			||||||
			return fmt.Errorf("create payload for %s[%s]: %v", w.Type, event, err)
 | 
								return fmt.Errorf("create payload for %s[%s]: %v", w.Type, event, err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		p.SetSecret(w.Secret)
 | 
					 | 
				
			||||||
		payloader = p
 | 
							payloader = p
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var signature string
 | 
					 | 
				
			||||||
	if len(w.Secret) > 0 {
 | 
					 | 
				
			||||||
		data, err := payloader.JSONPayload()
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			log.Error("prepareWebhooks.JSONPayload: %v", err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		sig := hmac.New(sha256.New, []byte(w.Secret))
 | 
					 | 
				
			||||||
		_, err = sig.Write(data)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			log.Error("prepareWebhooks.sigWrite: %v", err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		signature = hex.EncodeToString(sig.Sum(nil))
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err = models.CreateHookTask(&models.HookTask{
 | 
						if err = models.CreateHookTask(&models.HookTask{
 | 
				
			||||||
		RepoID:    repo.ID,
 | 
							RepoID:    repo.ID,
 | 
				
			||||||
		HookID:    w.ID,
 | 
							HookID:    w.ID,
 | 
				
			||||||
		Typ:         w.Type,
 | 
					 | 
				
			||||||
		URL:         w.URL,
 | 
					 | 
				
			||||||
		Signature:   signature,
 | 
					 | 
				
			||||||
		Payloader: payloader,
 | 
							Payloader: payloader,
 | 
				
			||||||
		HTTPMethod:  w.HTTPMethod,
 | 
					 | 
				
			||||||
		ContentType: w.ContentType,
 | 
					 | 
				
			||||||
		EventType: event,
 | 
							EventType: event,
 | 
				
			||||||
		IsSSL:       w.IsSSL,
 | 
					 | 
				
			||||||
	}); err != nil {
 | 
						}); err != nil {
 | 
				
			||||||
		return fmt.Errorf("CreateHookTask: %v", err)
 | 
							return fmt.Errorf("CreateHookTask: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -44,8 +44,8 @@
 | 
				
			||||||
						<div class="ui bottom attached tab segment active" data-tab="request-{{.ID}}">
 | 
											<div class="ui bottom attached tab segment active" data-tab="request-{{.ID}}">
 | 
				
			||||||
							{{if .RequestInfo}}
 | 
												{{if .RequestInfo}}
 | 
				
			||||||
								<h5>{{$.i18n.Tr "repo.settings.webhook.headers"}}</h5>
 | 
													<h5>{{$.i18n.Tr "repo.settings.webhook.headers"}}</h5>
 | 
				
			||||||
								<pre class="webhook-info"><strong>Request URL:</strong> {{.URL}}
 | 
													<pre class="webhook-info"><strong>Request URL:</strong> {{.RequestInfo.URL}}
 | 
				
			||||||
<strong>Request method:</strong> {{if .HTTPMethod}}{{.HTTPMethod}}{{else}}POST{{end}}
 | 
					<strong>Request method:</strong> {{if .RequestInfo.HTTPMethod}}{{.RequestInfo.HTTPMethod}}{{else}}POST{{end}}
 | 
				
			||||||
{{ range $key, $val := .RequestInfo.Headers }}<strong>{{$key}}:</strong> {{$val}}
 | 
					{{ range $key, $val := .RequestInfo.Headers }}<strong>{{$key}}:</strong> {{$val}}
 | 
				
			||||||
{{end}}</pre>
 | 
					{{end}}</pre>
 | 
				
			||||||
								<h5>{{$.i18n.Tr "repo.settings.webhook.payload"}}</h5>
 | 
													<h5>{{$.i18n.Tr "repo.settings.webhook.payload"}}</h5>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue