mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-11-04 01:34:27 +00:00 
			
		
		
		
	Don't use legacy method to send Matrix Webhook (#12348)
* Don't use legacy send for messages * Add migrations to ensure Matrix webhooks use PUT * Set HTTP method to PUT as default * Fix sql condition.. Signed-off-by: Till Faelligen <tfaelligen@gmail.com> * Rename getTxnID -> getMatrixTxnID * Use local variable instead of constant value Co-authored-by: techknowlogick <techknowlogick@gitea.io>
This commit is contained in:
		@@ -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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										25
									
								
								models/migrations/v144.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								models/migrations/v144.go
									
									
									
									
									
										Normal file
									
								
							@@ -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/modules/log"
 | 
			
		||||
	"xorm.io/builder"
 | 
			
		||||
	"xorm.io/xorm"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func updateMatrixWebhookHTTPMethod(x *xorm.Engine) error {
 | 
			
		||||
	var matrixHookTaskType = 9 // value comes from the models package
 | 
			
		||||
	type Webhook struct {
 | 
			
		||||
		HTTPMethod string
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cond := builder.Eq{"hook_task_type": matrixHookTaskType}.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
 | 
			
		||||
}
 | 
			
		||||
@@ -77,15 +77,18 @@ func Deliver(t *models.HookTask) error {
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	default:
 | 
			
		||||
		return fmt.Errorf("Invalid http method for webhook: [%d] %v", t.ID, t.HTTPMethod)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if t.Type == models.MATRIX {
 | 
			
		||||
	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)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	req.Header.Add("X-Gitea-Delivery", t.UUID)
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@
 | 
			
		||||
package webhook
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/sha1"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
@@ -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 := getMatrixTxnID(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
 | 
			
		||||
	}
 | 
			
		||||
@@ -301,3 +309,14 @@ func getMatrixHookRequest(t *models.HookTask) (*http.Request, error) {
 | 
			
		||||
 | 
			
		||||
	return req, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// getMatrixTxnID creates a txnID based on the payload to ensure idempotency
 | 
			
		||||
func getMatrixTxnID(payload []byte) (string, error) {
 | 
			
		||||
	h := sha1.New()
 | 
			
		||||
	_, err := h.Write(payload)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return fmt.Sprintf("%x", h.Sum(nil)), nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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 := getMatrixTxnID(tt.args.payload)
 | 
			
		||||
			if (err != nil) != tt.wantErr {
 | 
			
		||||
				t.Errorf("getMatrixTxnID() error = %v, wantErr %v", err, tt.wantErr)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			assert.Equal(t, tt.want, got)
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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,
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user