mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-29 10:57:44 +09: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