Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions models/migrations/migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,8 @@ var migrations = []Migration{
NewMigration("Ensure Repository.IsArchived is not null", setIsArchivedToFalse),
// v143 -> v144
NewMigration("recalculate Stars number for all user", recalculateStars),
// v144 -> v145
NewMigration("update Matrix Webhook http method to 'PUT'", updateMatrixWebhookHTTPMethod),
}

// GetCurrentDBVersion returns the current db version
Expand Down
25 changes: 25 additions & 0 deletions models/migrations/v144.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright 2020 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 (
"code.gitea.io/gitea/models"
Comment thread
S7evinK marked this conversation as resolved.
Outdated
"code.gitea.io/gitea/modules/log"
"xorm.io/builder"
"xorm.io/xorm"
)

func updateMatrixWebhookHTTPMethod(x *xorm.Engine) error {
type Webhook struct {
HTTPMethod string
}

cond := builder.Eq{"hook_task_type": models.MATRIX}.And(builder.Neq{"http_method": "PUT"})
count, err := x.Where(cond).Cols("http_method").Update(&Webhook{HTTPMethod: "PUT"})
if err == nil {
log.Debug("Updated %d Matrix webhooks with http_method 'PUT'", count)
}
return err
}
17 changes: 10 additions & 7 deletions modules/webhook/deliver.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,17 +77,20 @@ func Deliver(t *models.HookTask) error {
if err != nil {
return err
}
case http.MethodPut:
switch t.Type {
case models.MATRIX:
req, err = getMatrixHookRequest(t)
if err != nil {
return err
}
default:
return fmt.Errorf("Invalid http method for webhook: [%d] %v", t.ID, t.HTTPMethod)
}
default:
return fmt.Errorf("Invalid http method for webhook: [%d] %v", t.ID, t.HTTPMethod)
}

if t.Type == models.MATRIX {
req, err = getMatrixHookRequest(t)
if err != nil {
return err
}
}

req.Header.Add("X-Gitea-Delivery", t.UUID)
req.Header.Add("X-Gitea-Event", t.EventType.Event())
req.Header.Add("X-Gitea-Signature", t.Signature)
Expand Down
21 changes: 20 additions & 1 deletion modules/webhook/matrix.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package webhook

import (
"crypto/sha1"
"encoding/json"
"errors"
"fmt"
Expand Down Expand Up @@ -291,7 +292,14 @@ func getMatrixHookRequest(t *models.HookTask) (*http.Request, error) {
}
t.PayloadContent = string(payload)

req, err := http.NewRequest("POST", t.URL, strings.NewReader(string(payload)))
txnID, err := getTxnID(payload)
if err != nil {
return nil, fmt.Errorf("getMatrixHookRequest: unable to hash payload: %+v", err)
}

t.URL = fmt.Sprintf("%s/%s", t.URL, txnID)

req, err := http.NewRequest(t.HTTPMethod, t.URL, strings.NewReader(string(payload)))
if err != nil {
return nil, err
}
Expand All @@ -301,3 +309,14 @@ func getMatrixHookRequest(t *models.HookTask) (*http.Request, error) {

return req, nil
}

// getTxnID creates a txnID based on the payload to ensure idempotency
func getTxnID(payload []byte) (string, error) {
Comment thread
S7evinK marked this conversation as resolved.
Outdated
h := sha1.New()
_, err := h.Write(payload)
if err != nil {
return "", err
}

return fmt.Sprintf("%x", h.Sum(nil)), nil
}
29 changes: 29 additions & 0 deletions modules/webhook/matrix_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,3 +154,32 @@ func TestMatrixHookRequest(t *testing.T) {
assert.Equal(t, "Bearer dummy_access_token", req.Header.Get("Authorization"))
assert.Equal(t, wantPayloadContent, h.PayloadContent)
}

func Test_getTxnID(t *testing.T) {
type args struct {
payload []byte
}
tests := []struct {
name string
args args
want string
wantErr bool
}{
{
name: "dummy payload",
args: args{payload: []byte("Hello World")},
want: "0a4d55a8d778e5022fab701977c5d840bbc486d0",
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := getTxnID(tt.args.payload)
if (err != nil) != tt.wantErr {
t.Errorf("getTxnID() error = %v, wantErr %v", err, tt.wantErr)
return
}
assert.Equal(t, tt.want, got)
})
}
}
1 change: 1 addition & 0 deletions routers/repo/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,7 @@ func MatrixHooksNewPost(ctx *context.Context, form auth.NewMatrixHookForm) {
RepoID: orCtx.RepoID,
URL: fmt.Sprintf("%s/_matrix/client/r0/rooms/%s/send/m.room.message", form.HomeserverURL, form.RoomID),
ContentType: models.ContentTypeJSON,
HTTPMethod: "PUT",
HookEvent: ParseHookEvent(form.WebhookForm),
IsActive: form.Active,
HookTaskType: models.MATRIX,
Expand Down