diff options
author | oliverpool <3864879+oliverpool@users.noreply.github.com> | 2022-11-03 19:23:20 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-11-03 20:23:20 +0200 |
commit | b6e81357bd6fb80f8ba94c513f89a210beb05313 (patch) | |
tree | b495f05c492c4876a5f8ded3a85d9985e0ec22a8 /services/webhook | |
parent | 085f717529008c31b147f76ea7eeaf06ca8801bd (diff) | |
download | gitea-b6e81357bd6fb80f8ba94c513f89a210beb05313.tar.gz gitea-b6e81357bd6fb80f8ba94c513f89a210beb05313.zip |
Add Webhook authorization header (#20926)
_This is a different approach to #20267, I took the liberty of adapting
some parts, see below_
## Context
In some cases, a weebhook endpoint requires some kind of authentication.
The usual way is by sending a static `Authorization` header, with a
given token. For instance:
- Matrix expects a `Bearer <token>` (already implemented, by storing the
header cleartext in the metadata - which is buggy on retry #19872)
- TeamCity #18667
- Gitea instances #20267
- SourceHut https://man.sr.ht/graphql.md#authentication-strategies (this
is my actual personal need :)
## Proposed solution
Add a dedicated encrypt column to the webhook table (instead of storing
it as meta as proposed in #20267), so that it gets available for all
present and future hook types (especially the custom ones #19307).
This would also solve the buggy matrix retry #19872.
As a first step, I would recommend focusing on the backend logic and
improve the frontend at a later stage. For now the UI is a simple
`Authorization` field (which could be later customized with `Bearer` and
`Basic` switches):
![2022-08-23-142911](https://user-images.githubusercontent.com/3864879/186162483-5b721504-eef5-4932-812e-eb96a68494cc.png)
The header name is hard-coded, since I couldn't fine any usecase
justifying otherwise.
## Questions
- What do you think of this approach? @justusbunsi @Gusted @silverwind
- ~~How are the migrations generated? Do I have to manually create a new
file, or is there a command for that?~~
- ~~I started adding it to the API: should I complete it or should I
drop it? (I don't know how much the API is actually used)~~
## Done as well:
- add a migration for the existing matrix webhooks and remove the
`Authorization` logic there
_Closes #19872_
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Gusted <williamzijl7@hotmail.com>
Co-authored-by: delvh <dev.lh@web.de>
Diffstat (limited to 'services/webhook')
-rw-r--r-- | services/webhook/deliver.go | 17 | ||||
-rw-r--r-- | services/webhook/deliver_test.go | 41 | ||||
-rw-r--r-- | services/webhook/main_test.go | 4 | ||||
-rw-r--r-- | services/webhook/matrix.go | 127 | ||||
-rw-r--r-- | services/webhook/matrix_test.go | 190 |
5 files changed, 156 insertions, 223 deletions
diff --git a/services/webhook/deliver.go b/services/webhook/deliver.go index 74a69c297c..85717e0978 100644 --- a/services/webhook/deliver.go +++ b/services/webhook/deliver.go @@ -90,7 +90,12 @@ func Deliver(ctx context.Context, t *webhook_model.HookTask) error { case http.MethodPut: switch w.Type { case webhook_model.MATRIX: - req, err = getMatrixHookRequest(w, t) + txnID, err := getMatrixTxnID([]byte(t.PayloadContent)) + if err != nil { + return err + } + url := fmt.Sprintf("%s/%s", w.URL, url.PathEscape(txnID)) + req, err = http.NewRequest("PUT", url, strings.NewReader(t.PayloadContent)) if err != nil { return err } @@ -130,6 +135,16 @@ func Deliver(ctx context.Context, t *webhook_model.HookTask) error { req.Header["X-GitHub-Event"] = []string{event} req.Header["X-GitHub-Event-Type"] = []string{eventType} + // Add Authorization Header + authorization, err := w.HeaderAuthorization() + if err != nil { + log.Error("Webhook could not get Authorization header [%d]: %v", w.ID, err) + return err + } + if authorization != "" { + req.Header["Authorization"] = []string{authorization} + } + // Record delivery information. t.RequestInfo = &webhook_model.HookRequest{ URL: req.URL.String(), diff --git a/services/webhook/deliver_test.go b/services/webhook/deliver_test.go index 8d1d587c38..83ca7d6178 100644 --- a/services/webhook/deliver_test.go +++ b/services/webhook/deliver_test.go @@ -5,10 +5,16 @@ package webhook import ( + "context" "net/http" + "net/http/httptest" "net/url" "testing" + "time" + "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/models/unittest" + webhook_model "code.gitea.io/gitea/models/webhook" "code.gitea.io/gitea/modules/setting" "github.com/stretchr/testify/assert" @@ -38,3 +44,38 @@ func TestWebhookProxy(t *testing.T) { } } } + +func TestWebhookDeliverAuthorizationHeader(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + + done := make(chan struct{}, 1) + s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, "/webhook", r.URL.Path) + assert.Equal(t, "Bearer s3cr3t-t0ken", r.Header.Get("Authorization")) + w.WriteHeader(200) + done <- struct{}{} + })) + t.Cleanup(s.Close) + + hook := &webhook_model.Webhook{ + RepoID: 3, + URL: s.URL + "/webhook", + ContentType: webhook_model.ContentTypeJSON, + IsActive: true, + Type: webhook_model.GITEA, + } + err := hook.SetHeaderAuthorization("Bearer s3cr3t-t0ken") + assert.NoError(t, err) + assert.NoError(t, webhook_model.CreateWebhook(db.DefaultContext, hook)) + + hookTask := &webhook_model.HookTask{HookID: hook.ID, EventType: webhook_model.HookEventPush} + + assert.NoError(t, Deliver(context.Background(), hookTask)) + select { + case <-done: + case <-time.After(5 * time.Second): + t.Fatal("waited to long for request to happen") + } + + assert.True(t, hookTask.IsSucceed) +} diff --git a/services/webhook/main_test.go b/services/webhook/main_test.go index 1dc2e1bd83..5ddb6cf1f3 100644 --- a/services/webhook/main_test.go +++ b/services/webhook/main_test.go @@ -9,6 +9,7 @@ import ( "testing" "code.gitea.io/gitea/models/unittest" + "code.gitea.io/gitea/modules/hostmatcher" "code.gitea.io/gitea/modules/setting" _ "code.gitea.io/gitea/models" @@ -17,6 +18,9 @@ import ( func TestMain(m *testing.M) { setting.LoadForTest() setting.NewQueueService() + + // for tests, allow only loopback IPs + setting.Webhook.AllowedHostList = hostmatcher.MatchBuiltinLoopback unittest.MainTest(m, &unittest.TestOptions{ GiteaRootPath: filepath.Join("..", ".."), SetUp: Init, diff --git a/services/webhook/matrix.go b/services/webhook/matrix.go index 6bbae70422..7ff8b1e638 100644 --- a/services/webhook/matrix.go +++ b/services/webhook/matrix.go @@ -6,10 +6,10 @@ package webhook import ( "crypto/sha1" + "encoding/hex" "errors" "fmt" "html" - "net/http" "net/url" "regexp" "strings" @@ -29,7 +29,6 @@ const matrixPayloadSizeLimit = 1024 * 64 type MatrixMeta struct { HomeserverURL string `json:"homeserver_url"` Room string `json:"room_id"` - AccessToken string `json:"access_token"` MessageType int `json:"message_type"` } @@ -47,27 +46,10 @@ func GetMatrixHook(w *webhook_model.Webhook) *MatrixMeta { return s } -// MatrixPayloadUnsafe contains the (unsafe) payload for a Matrix room -type MatrixPayloadUnsafe struct { - MatrixPayloadSafe - AccessToken string `json:"access_token"` -} - -var _ PayloadConvertor = &MatrixPayloadUnsafe{} - -// safePayload "converts" a unsafe payload to a safe payload -func (m *MatrixPayloadUnsafe) safePayload() *MatrixPayloadSafe { - return &MatrixPayloadSafe{ - Body: m.Body, - MsgType: m.MsgType, - Format: m.Format, - FormattedBody: m.FormattedBody, - Commits: m.Commits, - } -} +var _ PayloadConvertor = &MatrixPayload{} -// MatrixPayloadSafe contains (safe) payload for a Matrix room -type MatrixPayloadSafe struct { +// MatrixPayload contains payload for a Matrix room +type MatrixPayload struct { Body string `json:"body"` MsgType string `json:"msgtype"` Format string `json:"format"` @@ -75,8 +57,8 @@ type MatrixPayloadSafe struct { Commits []*api.PayloadCommit `json:"io.gitea.commits,omitempty"` } -// JSONPayload Marshals the MatrixPayloadUnsafe to json -func (m *MatrixPayloadUnsafe) JSONPayload() ([]byte, error) { +// JSONPayload Marshals the MatrixPayload to json +func (m *MatrixPayload) JSONPayload() ([]byte, error) { data, err := json.MarshalIndent(m, "", " ") if err != nil { return []byte{}, err @@ -103,62 +85,62 @@ func MatrixLinkToRef(repoURL, ref string) string { } // Create implements PayloadConvertor Create method -func (m *MatrixPayloadUnsafe) Create(p *api.CreatePayload) (api.Payloader, error) { +func (m *MatrixPayload) Create(p *api.CreatePayload) (api.Payloader, error) { repoLink := MatrixLinkFormatter(p.Repo.HTMLURL, p.Repo.FullName) refLink := MatrixLinkToRef(p.Repo.HTMLURL, p.Ref) text := fmt.Sprintf("[%s:%s] %s created by %s", repoLink, refLink, p.RefType, p.Sender.UserName) - return getMatrixPayloadUnsafe(text, nil, m.AccessToken, m.MsgType), nil + return getMatrixPayload(text, nil, m.MsgType), nil } // Delete composes Matrix payload for delete a branch or tag. -func (m *MatrixPayloadUnsafe) Delete(p *api.DeletePayload) (api.Payloader, error) { +func (m *MatrixPayload) Delete(p *api.DeletePayload) (api.Payloader, error) { refName := git.RefEndName(p.Ref) repoLink := MatrixLinkFormatter(p.Repo.HTMLURL, p.Repo.FullName) text := fmt.Sprintf("[%s:%s] %s deleted by %s", repoLink, refName, p.RefType, p.Sender.UserName) - return getMatrixPayloadUnsafe(text, nil, m.AccessToken, m.MsgType), nil + return getMatrixPayload(text, nil, m.MsgType), nil } // Fork composes Matrix payload for forked by a repository. -func (m *MatrixPayloadUnsafe) Fork(p *api.ForkPayload) (api.Payloader, error) { +func (m *MatrixPayload) Fork(p *api.ForkPayload) (api.Payloader, error) { baseLink := MatrixLinkFormatter(p.Forkee.HTMLURL, p.Forkee.FullName) forkLink := MatrixLinkFormatter(p.Repo.HTMLURL, p.Repo.FullName) text := fmt.Sprintf("%s is forked to %s", baseLink, forkLink) - return getMatrixPayloadUnsafe(text, nil, m.AccessToken, m.MsgType), nil + return getMatrixPayload(text, nil, m.MsgType), nil } // Issue implements PayloadConvertor Issue method -func (m *MatrixPayloadUnsafe) Issue(p *api.IssuePayload) (api.Payloader, error) { +func (m *MatrixPayload) Issue(p *api.IssuePayload) (api.Payloader, error) { text, _, _, _ := getIssuesPayloadInfo(p, MatrixLinkFormatter, true) - return getMatrixPayloadUnsafe(text, nil, m.AccessToken, m.MsgType), nil + return getMatrixPayload(text, nil, m.MsgType), nil } // IssueComment implements PayloadConvertor IssueComment method -func (m *MatrixPayloadUnsafe) IssueComment(p *api.IssueCommentPayload) (api.Payloader, error) { +func (m *MatrixPayload) IssueComment(p *api.IssueCommentPayload) (api.Payloader, error) { text, _, _ := getIssueCommentPayloadInfo(p, MatrixLinkFormatter, true) - return getMatrixPayloadUnsafe(text, nil, m.AccessToken, m.MsgType), nil + return getMatrixPayload(text, nil, m.MsgType), nil } // Wiki implements PayloadConvertor Wiki method -func (m *MatrixPayloadUnsafe) Wiki(p *api.WikiPayload) (api.Payloader, error) { +func (m *MatrixPayload) Wiki(p *api.WikiPayload) (api.Payloader, error) { text, _, _ := getWikiPayloadInfo(p, MatrixLinkFormatter, true) - return getMatrixPayloadUnsafe(text, nil, m.AccessToken, m.MsgType), nil + return getMatrixPayload(text, nil, m.MsgType), nil } // Release implements PayloadConvertor Release method -func (m *MatrixPayloadUnsafe) Release(p *api.ReleasePayload) (api.Payloader, error) { +func (m *MatrixPayload) Release(p *api.ReleasePayload) (api.Payloader, error) { text, _ := getReleasePayloadInfo(p, MatrixLinkFormatter, true) - return getMatrixPayloadUnsafe(text, nil, m.AccessToken, m.MsgType), nil + return getMatrixPayload(text, nil, m.MsgType), nil } // Push implements PayloadConvertor Push method -func (m *MatrixPayloadUnsafe) Push(p *api.PushPayload) (api.Payloader, error) { +func (m *MatrixPayload) Push(p *api.PushPayload) (api.Payloader, error) { var commitDesc string if p.TotalCommits == 1 { @@ -181,18 +163,18 @@ func (m *MatrixPayloadUnsafe) Push(p *api.PushPayload) (api.Payloader, error) { } - return getMatrixPayloadUnsafe(text, p.Commits, m.AccessToken, m.MsgType), nil + return getMatrixPayload(text, p.Commits, m.MsgType), nil } // PullRequest implements PayloadConvertor PullRequest method -func (m *MatrixPayloadUnsafe) PullRequest(p *api.PullRequestPayload) (api.Payloader, error) { +func (m *MatrixPayload) PullRequest(p *api.PullRequestPayload) (api.Payloader, error) { text, _, _, _ := getPullRequestPayloadInfo(p, MatrixLinkFormatter, true) - return getMatrixPayloadUnsafe(text, nil, m.AccessToken, m.MsgType), nil + return getMatrixPayload(text, nil, m.MsgType), nil } // Review implements PayloadConvertor Review method -func (m *MatrixPayloadUnsafe) Review(p *api.PullRequestPayload, event webhook_model.HookEventType) (api.Payloader, error) { +func (m *MatrixPayload) Review(p *api.PullRequestPayload, event webhook_model.HookEventType) (api.Payloader, error) { senderLink := MatrixLinkFormatter(setting.AppURL+url.PathEscape(p.Sender.UserName), p.Sender.UserName) title := fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title) titleLink := MatrixLinkFormatter(p.PullRequest.URL, title) @@ -209,11 +191,11 @@ func (m *MatrixPayloadUnsafe) Review(p *api.PullRequestPayload, event webhook_mo text = fmt.Sprintf("[%s] Pull request review %s: %s by %s", repoLink, action, titleLink, senderLink) } - return getMatrixPayloadUnsafe(text, nil, m.AccessToken, m.MsgType), nil + return getMatrixPayload(text, nil, m.MsgType), nil } // Repository implements PayloadConvertor Repository method -func (m *MatrixPayloadUnsafe) Repository(p *api.RepositoryPayload) (api.Payloader, error) { +func (m *MatrixPayload) Repository(p *api.RepositoryPayload) (api.Payloader, error) { senderLink := MatrixLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName) repoLink := MatrixLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName) var text string @@ -225,27 +207,25 @@ func (m *MatrixPayloadUnsafe) Repository(p *api.RepositoryPayload) (api.Payloade text = fmt.Sprintf("[%s] Repository deleted by %s", repoLink, senderLink) } - return getMatrixPayloadUnsafe(text, nil, m.AccessToken, m.MsgType), nil + return getMatrixPayload(text, nil, m.MsgType), nil } -// GetMatrixPayload converts a Matrix webhook into a MatrixPayloadUnsafe +// GetMatrixPayload converts a Matrix webhook into a MatrixPayload func GetMatrixPayload(p api.Payloader, event webhook_model.HookEventType, meta string) (api.Payloader, error) { - s := new(MatrixPayloadUnsafe) + s := new(MatrixPayload) matrix := &MatrixMeta{} if err := json.Unmarshal([]byte(meta), &matrix); err != nil { return s, errors.New("GetMatrixPayload meta json:" + err.Error()) } - s.AccessToken = matrix.AccessToken s.MsgType = messageTypeText[matrix.MessageType] return convertPayloader(s, p, event) } -func getMatrixPayloadUnsafe(text string, commits []*api.PayloadCommit, accessToken, msgType string) *MatrixPayloadUnsafe { - p := MatrixPayloadUnsafe{} - p.AccessToken = accessToken +func getMatrixPayload(text string, commits []*api.PayloadCommit, msgType string) *MatrixPayload { + p := MatrixPayload{} p.FormattedBody = text p.Body = getMessageBody(text) p.Format = "org.matrix.custom.html" @@ -262,52 +242,17 @@ func getMessageBody(htmlText string) string { return htmlText } -// getMatrixHookRequest creates a new request which contains an Authorization header. -// The access_token is removed from t.PayloadContent -func getMatrixHookRequest(w *webhook_model.Webhook, t *webhook_model.HookTask) (*http.Request, error) { - payloadunsafe := MatrixPayloadUnsafe{} - if err := json.Unmarshal([]byte(t.PayloadContent), &payloadunsafe); err != nil { - log.Error("Matrix Hook delivery failed: %v", err) - return nil, err - } - - payloadsafe := payloadunsafe.safePayload() - - var payload []byte - var err error - if payload, err = json.MarshalIndent(payloadsafe, "", " "); err != nil { - return nil, err - } +// getMatrixTxnID computes the transaction ID to ensure idempotency +func getMatrixTxnID(payload []byte) (string, error) { if len(payload) >= matrixPayloadSizeLimit { - return nil, fmt.Errorf("getMatrixHookRequest: payload size %d > %d", len(payload), matrixPayloadSizeLimit) + return "", fmt.Errorf("getMatrixTxnID: payload size %d > %d", len(payload), matrixPayloadSizeLimit) } - t.PayloadContent = string(payload) - txnID, err := getMatrixTxnID(payload) - if err != nil { - return nil, fmt.Errorf("getMatrixHookRequest: unable to hash payload: %+v", err) - } - - url := fmt.Sprintf("%s/%s", w.URL, url.PathEscape(txnID)) - - req, err := http.NewRequest(w.HTTPMethod, url, strings.NewReader(string(payload))) - if err != nil { - return nil, err - } - - req.Header.Set("Content-Type", "application/json") - req.Header.Add("Authorization", "Bearer "+payloadunsafe.AccessToken) - - 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 + return hex.EncodeToString(h.Sum(nil)), nil } diff --git a/services/webhook/matrix_test.go b/services/webhook/matrix_test.go index 624986ee9b..bbcdef3567 100644 --- a/services/webhook/matrix_test.go +++ b/services/webhook/matrix_test.go @@ -18,275 +18,203 @@ func TestMatrixPayload(t *testing.T) { t.Run("Create", func(t *testing.T) { p := createTestPayload() - d := new(MatrixPayloadUnsafe) + d := new(MatrixPayload) pl, err := d.Create(p) require.NoError(t, err) require.NotNil(t, pl) - require.IsType(t, &MatrixPayloadUnsafe{}, pl) + require.IsType(t, &MatrixPayload{}, pl) - assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo):[test](http://localhost:3000/test/repo/src/branch/test)] branch created by user1", pl.(*MatrixPayloadUnsafe).Body) - assert.Equal(t, `[<a href="http://localhost:3000/test/repo">test/repo</a>:<a href="http://localhost:3000/test/repo/src/branch/test">test</a>] branch created by user1`, pl.(*MatrixPayloadUnsafe).FormattedBody) + assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo):[test](http://localhost:3000/test/repo/src/branch/test)] branch created by user1", pl.(*MatrixPayload).Body) + assert.Equal(t, `[<a href="http://localhost:3000/test/repo">test/repo</a>:<a href="http://localhost:3000/test/repo/src/branch/test">test</a>] branch created by user1`, pl.(*MatrixPayload).FormattedBody) }) t.Run("Delete", func(t *testing.T) { p := deleteTestPayload() - d := new(MatrixPayloadUnsafe) + d := new(MatrixPayload) pl, err := d.Delete(p) require.NoError(t, err) require.NotNil(t, pl) - require.IsType(t, &MatrixPayloadUnsafe{}, pl) + require.IsType(t, &MatrixPayload{}, pl) - assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo):test] branch deleted by user1", pl.(*MatrixPayloadUnsafe).Body) - assert.Equal(t, `[<a href="http://localhost:3000/test/repo">test/repo</a>:test] branch deleted by user1`, pl.(*MatrixPayloadUnsafe).FormattedBody) + assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo):test] branch deleted by user1", pl.(*MatrixPayload).Body) + assert.Equal(t, `[<a href="http://localhost:3000/test/repo">test/repo</a>:test] branch deleted by user1`, pl.(*MatrixPayload).FormattedBody) }) t.Run("Fork", func(t *testing.T) { p := forkTestPayload() - d := new(MatrixPayloadUnsafe) + d := new(MatrixPayload) pl, err := d.Fork(p) require.NoError(t, err) require.NotNil(t, pl) - require.IsType(t, &MatrixPayloadUnsafe{}, pl) + require.IsType(t, &MatrixPayload{}, pl) - assert.Equal(t, "[test/repo2](http://localhost:3000/test/repo2) is forked to [test/repo](http://localhost:3000/test/repo)", pl.(*MatrixPayloadUnsafe).Body) - assert.Equal(t, `<a href="http://localhost:3000/test/repo2">test/repo2</a> is forked to <a href="http://localhost:3000/test/repo">test/repo</a>`, pl.(*MatrixPayloadUnsafe).FormattedBody) + assert.Equal(t, "[test/repo2](http://localhost:3000/test/repo2) is forked to [test/repo](http://localhost:3000/test/repo)", pl.(*MatrixPayload).Body) + assert.Equal(t, `<a href="http://localhost:3000/test/repo2">test/repo2</a> is forked to <a href="http://localhost:3000/test/repo">test/repo</a>`, pl.(*MatrixPayload).FormattedBody) }) t.Run("Push", func(t *testing.T) { p := pushTestPayload() - d := new(MatrixPayloadUnsafe) + d := new(MatrixPayload) pl, err := d.Push(p) require.NoError(t, err) require.NotNil(t, pl) - require.IsType(t, &MatrixPayloadUnsafe{}, pl) + require.IsType(t, &MatrixPayload{}, pl) - assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] user1 pushed 2 commits to [test](http://localhost:3000/test/repo/src/branch/test):\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778): commit message - user1\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778): commit message - user1", pl.(*MatrixPayloadUnsafe).Body) - assert.Equal(t, `[<a href="http://localhost:3000/test/repo">test/repo</a>] user1 pushed 2 commits to <a href="http://localhost:3000/test/repo/src/branch/test">test</a>:<br><a href="http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778">2020558</a>: commit message - user1<br><a href="http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778">2020558</a>: commit message - user1`, pl.(*MatrixPayloadUnsafe).FormattedBody) + assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] user1 pushed 2 commits to [test](http://localhost:3000/test/repo/src/branch/test):\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778): commit message - user1\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778): commit message - user1", pl.(*MatrixPayload).Body) + assert.Equal(t, `[<a href="http://localhost:3000/test/repo">test/repo</a>] user1 pushed 2 commits to <a href="http://localhost:3000/test/repo/src/branch/test">test</a>:<br><a href="http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778">2020558</a>: commit message - user1<br><a href="http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778">2020558</a>: commit message - user1`, pl.(*MatrixPayload).FormattedBody) }) t.Run("Issue", func(t *testing.T) { p := issueTestPayload() - d := new(MatrixPayloadUnsafe) + d := new(MatrixPayload) p.Action = api.HookIssueOpened pl, err := d.Issue(p) require.NoError(t, err) require.NotNil(t, pl) - require.IsType(t, &MatrixPayloadUnsafe{}, pl) + require.IsType(t, &MatrixPayload{}, pl) - assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Issue opened: [#2 crash](http://localhost:3000/test/repo/issues/2) by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayloadUnsafe).Body) - assert.Equal(t, `[<a href="http://localhost:3000/test/repo">test/repo</a>] Issue opened: <a href="http://localhost:3000/test/repo/issues/2">#2 crash</a> by <a href="https://try.gitea.io/user1">user1</a>`, pl.(*MatrixPayloadUnsafe).FormattedBody) + assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Issue opened: [#2 crash](http://localhost:3000/test/repo/issues/2) by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayload).Body) + assert.Equal(t, `[<a href="http://localhost:3000/test/repo">test/repo</a>] Issue opened: <a href="http://localhost:3000/test/repo/issues/2">#2 crash</a> by <a href="https://try.gitea.io/user1">user1</a>`, pl.(*MatrixPayload).FormattedBody) p.Action = api.HookIssueClosed pl, err = d.Issue(p) require.NoError(t, err) require.NotNil(t, pl) - require.IsType(t, &MatrixPayloadUnsafe{}, pl) + require.IsType(t, &MatrixPayload{}, pl) - assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Issue closed: [#2 crash](http://localhost:3000/test/repo/issues/2) by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayloadUnsafe).Body) - assert.Equal(t, `[<a href="http://localhost:3000/test/repo">test/repo</a>] Issue closed: <a href="http://localhost:3000/test/repo/issues/2">#2 crash</a> by <a href="https://try.gitea.io/user1">user1</a>`, pl.(*MatrixPayloadUnsafe).FormattedBody) + assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Issue closed: [#2 crash](http://localhost:3000/test/repo/issues/2) by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayload).Body) + assert.Equal(t, `[<a href="http://localhost:3000/test/repo">test/repo</a>] Issue closed: <a href="http://localhost:3000/test/repo/issues/2">#2 crash</a> by <a href="https://try.gitea.io/user1">user1</a>`, pl.(*MatrixPayload).FormattedBody) }) t.Run("IssueComment", func(t *testing.T) { p := issueCommentTestPayload() - d := new(MatrixPayloadUnsafe) + d := new(MatrixPayload) pl, err := d.IssueComment(p) require.NoError(t, err) require.NotNil(t, pl) - require.IsType(t, &MatrixPayloadUnsafe{}, pl) + require.IsType(t, &MatrixPayload{}, pl) - assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] New comment on issue [#2 crash](http://localhost:3000/test/repo/issues/2) by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayloadUnsafe).Body) - assert.Equal(t, `[<a href="http://localhost:3000/test/repo">test/repo</a>] New comment on issue <a href="http://localhost:3000/test/repo/issues/2">#2 crash</a> by <a href="https://try.gitea.io/user1">user1</a>`, pl.(*MatrixPayloadUnsafe).FormattedBody) + assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] New comment on issue [#2 crash](http://localhost:3000/test/repo/issues/2) by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayload).Body) + assert.Equal(t, `[<a href="http://localhost:3000/test/repo">test/repo</a>] New comment on issue <a href="http://localhost:3000/test/repo/issues/2">#2 crash</a> by <a href="https://try.gitea.io/user1">user1</a>`, pl.(*MatrixPayload).FormattedBody) }) t.Run("PullRequest", func(t *testing.T) { p := pullRequestTestPayload() - d := new(MatrixPayloadUnsafe) + d := new(MatrixPayload) pl, err := d.PullRequest(p) require.NoError(t, err) require.NotNil(t, pl) - require.IsType(t, &MatrixPayloadUnsafe{}, pl) + require.IsType(t, &MatrixPayload{}, pl) - assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Pull request opened: [#12 Fix bug](http://localhost:3000/test/repo/pulls/12) by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayloadUnsafe).Body) - assert.Equal(t, `[<a href="http://localhost:3000/test/repo">test/repo</a>] Pull request opened: <a href="http://localhost:3000/test/repo/pulls/12">#12 Fix bug</a> by <a href="https://try.gitea.io/user1">user1</a>`, pl.(*MatrixPayloadUnsafe).FormattedBody) + assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Pull request opened: [#12 Fix bug](http://localhost:3000/test/repo/pulls/12) by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayload).Body) + assert.Equal(t, `[<a href="http://localhost:3000/test/repo">test/repo</a>] Pull request opened: <a href="http://localhost:3000/test/repo/pulls/12">#12 Fix bug</a> by <a href="https://try.gitea.io/user1">user1</a>`, pl.(*MatrixPayload).FormattedBody) }) t.Run("PullRequestComment", func(t *testing.T) { p := pullRequestCommentTestPayload() - d := new(MatrixPayloadUnsafe) + d := new(MatrixPayload) pl, err := d.IssueComment(p) require.NoError(t, err) require.NotNil(t, pl) - require.IsType(t, &MatrixPayloadUnsafe{}, pl) + require.IsType(t, &MatrixPayload{}, pl) - assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] New comment on pull request [#12 Fix bug](http://localhost:3000/test/repo/pulls/12) by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayloadUnsafe).Body) - assert.Equal(t, `[<a href="http://localhost:3000/test/repo">test/repo</a>] New comment on pull request <a href="http://localhost:3000/test/repo/pulls/12">#12 Fix bug</a> by <a href="https://try.gitea.io/user1">user1</a>`, pl.(*MatrixPayloadUnsafe).FormattedBody) + assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] New comment on pull request [#12 Fix bug](http://localhost:3000/test/repo/pulls/12) by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayload).Body) + assert.Equal(t, `[<a href="http://localhost:3000/test/repo">test/repo</a>] New comment on pull request <a href="http://localhost:3000/test/repo/pulls/12">#12 Fix bug</a> by <a href="https://try.gitea.io/user1">user1</a>`, pl.(*MatrixPayload).FormattedBody) }) t.Run("Review", func(t *testing.T) { p := pullRequestTestPayload() p.Action = api.HookIssueReviewed - d := new(MatrixPayloadUnsafe) + d := new(MatrixPayload) pl, err := d.Review(p, webhook_model.HookEventPullRequestReviewApproved) require.NoError(t, err) require.NotNil(t, pl) - require.IsType(t, &MatrixPayloadUnsafe{}, pl) + require.IsType(t, &MatrixPayload{}, pl) - assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Pull request review approved: [#12 Fix bug](http://localhost:3000/test/repo/pulls/12) by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayloadUnsafe).Body) - assert.Equal(t, `[<a href="http://localhost:3000/test/repo">test/repo</a>] Pull request review approved: <a href="http://localhost:3000/test/repo/pulls/12">#12 Fix bug</a> by <a href="https://try.gitea.io/user1">user1</a>`, pl.(*MatrixPayloadUnsafe).FormattedBody) + assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Pull request review approved: [#12 Fix bug](http://localhost:3000/test/repo/pulls/12) by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayload).Body) + assert.Equal(t, `[<a href="http://localhost:3000/test/repo">test/repo</a>] Pull request review approved: <a href="http://localhost:3000/test/repo/pulls/12">#12 Fix bug</a> by <a href="https://try.gitea.io/user1">user1</a>`, pl.(*MatrixPayload).FormattedBody) }) t.Run("Repository", func(t *testing.T) { p := repositoryTestPayload() - d := new(MatrixPayloadUnsafe) + d := new(MatrixPayload) pl, err := d.Repository(p) require.NoError(t, err) require.NotNil(t, pl) - require.IsType(t, &MatrixPayloadUnsafe{}, pl) + require.IsType(t, &MatrixPayload{}, pl) - assert.Equal(t, `[[test/repo](http://localhost:3000/test/repo)] Repository created by [user1](https://try.gitea.io/user1)`, pl.(*MatrixPayloadUnsafe).Body) - assert.Equal(t, `[<a href="http://localhost:3000/test/repo">test/repo</a>] Repository created by <a href="https://try.gitea.io/user1">user1</a>`, pl.(*MatrixPayloadUnsafe).FormattedBody) + assert.Equal(t, `[[test/repo](http://localhost:3000/test/repo)] Repository created by [user1](https://try.gitea.io/user1)`, pl.(*MatrixPayload).Body) + assert.Equal(t, `[<a href="http://localhost:3000/test/repo">test/repo</a>] Repository created by <a href="https://try.gitea.io/user1">user1</a>`, pl.(*MatrixPayload).FormattedBody) }) t.Run("Wiki", func(t *testing.T) { p := wikiTestPayload() - d := new(MatrixPayloadUnsafe) + d := new(MatrixPayload) p.Action = api.HookWikiCreated pl, err := d.Wiki(p) require.NoError(t, err) require.NotNil(t, pl) - require.IsType(t, &MatrixPayloadUnsafe{}, pl) + require.IsType(t, &MatrixPayload{}, pl) - assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] New wiki page '[index](http://localhost:3000/test/repo/wiki/index)' (Wiki change comment) by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayloadUnsafe).Body) - assert.Equal(t, `[<a href="http://localhost:3000/test/repo">test/repo</a>] New wiki page '<a href="http://localhost:3000/test/repo/wiki/index">index</a>' (Wiki change comment) by <a href="https://try.gitea.io/user1">user1</a>`, pl.(*MatrixPayloadUnsafe).FormattedBody) + assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] New wiki page '[index](http://localhost:3000/test/repo/wiki/index)' (Wiki change comment) by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayload).Body) + assert.Equal(t, `[<a href="http://localhost:3000/test/repo">test/repo</a>] New wiki page '<a href="http://localhost:3000/test/repo/wiki/index">index</a>' (Wiki change comment) by <a href="https://try.gitea.io/user1">user1</a>`, pl.(*MatrixPayload).FormattedBody) p.Action = api.HookWikiEdited pl, err = d.Wiki(p) require.NoError(t, err) require.NotNil(t, pl) - require.IsType(t, &MatrixPayloadUnsafe{}, pl) + require.IsType(t, &MatrixPayload{}, pl) - assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Wiki page '[index](http://localhost:3000/test/repo/wiki/index)' edited (Wiki change comment) by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayloadUnsafe).Body) - assert.Equal(t, `[<a href="http://localhost:3000/test/repo">test/repo</a>] Wiki page '<a href="http://localhost:3000/test/repo/wiki/index">index</a>' edited (Wiki change comment) by <a href="https://try.gitea.io/user1">user1</a>`, pl.(*MatrixPayloadUnsafe).FormattedBody) + assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Wiki page '[index](http://localhost:3000/test/repo/wiki/index)' edited (Wiki change comment) by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayload).Body) + assert.Equal(t, `[<a href="http://localhost:3000/test/repo">test/repo</a>] Wiki page '<a href="http://localhost:3000/test/repo/wiki/index">index</a>' edited (Wiki change comment) by <a href="https://try.gitea.io/user1">user1</a>`, pl.(*MatrixPayload).FormattedBody) p.Action = api.HookWikiDeleted pl, err = d.Wiki(p) require.NoError(t, err) require.NotNil(t, pl) - require.IsType(t, &MatrixPayloadUnsafe{}, pl) + require.IsType(t, &MatrixPayload{}, pl) - assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Wiki page '[index](http://localhost:3000/test/repo/wiki/index)' deleted by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayloadUnsafe).Body) - assert.Equal(t, `[<a href="http://localhost:3000/test/repo">test/repo</a>] Wiki page '<a href="http://localhost:3000/test/repo/wiki/index">index</a>' deleted by <a href="https://try.gitea.io/user1">user1</a>`, pl.(*MatrixPayloadUnsafe).FormattedBody) + assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Wiki page '[index](http://localhost:3000/test/repo/wiki/index)' deleted by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayload).Body) + assert.Equal(t, `[<a href="http://localhost:3000/test/repo">test/repo</a>] Wiki page '<a href="http://localhost:3000/test/repo/wiki/index">index</a>' deleted by <a href="https://try.gitea.io/user1">user1</a>`, pl.(*MatrixPayload).FormattedBody) }) t.Run("Release", func(t *testing.T) { p := pullReleaseTestPayload() - d := new(MatrixPayloadUnsafe) + d := new(MatrixPayload) pl, err := d.Release(p) require.NoError(t, err) require.NotNil(t, pl) - require.IsType(t, &MatrixPayloadUnsafe{}, pl) + require.IsType(t, &MatrixPayload{}, pl) - assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Release created: [v1.0](http://localhost:3000/test/repo/releases/tag/v1.0) by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayloadUnsafe).Body) - assert.Equal(t, `[<a href="http://localhost:3000/test/repo">test/repo</a>] Release created: <a href="http://localhost:3000/test/repo/releases/tag/v1.0">v1.0</a> by <a href="https://try.gitea.io/user1">user1</a>`, pl.(*MatrixPayloadUnsafe).FormattedBody) + assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Release created: [v1.0](http://localhost:3000/test/repo/releases/tag/v1.0) by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayload).Body) + assert.Equal(t, `[<a href="http://localhost:3000/test/repo">test/repo</a>] Release created: <a href="http://localhost:3000/test/repo/releases/tag/v1.0">v1.0</a> by <a href="https://try.gitea.io/user1">user1</a>`, pl.(*MatrixPayload).FormattedBody) }) } func TestMatrixJSONPayload(t *testing.T) { p := pushTestPayload() - pl, err := new(MatrixPayloadUnsafe).Push(p) + pl, err := new(MatrixPayload).Push(p) require.NoError(t, err) require.NotNil(t, pl) - require.IsType(t, &MatrixPayloadUnsafe{}, pl) + require.IsType(t, &MatrixPayload{}, pl) json, err := pl.JSONPayload() require.NoError(t, err) assert.NotEmpty(t, json) } -func TestMatrixHookRequest(t *testing.T) { - w := &webhook_model.Webhook{} - - h := &webhook_model.HookTask{ - 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", - "msgtype": "m.notice", - "format": "org.matrix.custom.html", - "formatted_body": "[\u003ca href=\"http://localhost:3000/user1/test\"\u003euser1/test\u003c/a\u003e] user1 pushed 1 commit to \u003ca href=\"http://localhost:3000/user1/test/src/branch/master\"\u003emaster\u003c/a\u003e:\u003cbr\u003e\u003ca href=\"http://localhost:3000/user1/test/commit/5175ef26201c58b035a3404b3fe02b4e8d436eee\"\u003e5175ef2\u003c/a\u003e: 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", - "io.gitea.commits": [ - { - "id": "5175ef26201c58b035a3404b3fe02b4e8d436eee", - "message": "Merge pull request 'Change readme.md' (#2) from add-matrix-webhook into master\n\nReviewed-on: http://localhost:3000/user1/test/pulls/2\n", - "url": "http://localhost:3000/user1/test/commit/5175ef26201c58b035a3404b3fe02b4e8d436eee", - "author": { - "name": "user1", - "email": "user@mail.com", - "username": "" - }, - "committer": { - "name": "user1", - "email": "user@mail.com", - "username": "" - }, - "verification": null, - "timestamp": "0001-01-01T00:00:00Z", - "added": null, - "removed": null, - "modified": null - } - ], - "access_token": "dummy_access_token" -}`, - } - - wantPayloadContent := `{ - "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", - "msgtype": "m.notice", - "format": "org.matrix.custom.html", - "formatted_body": "[\u003ca href=\"http://localhost:3000/user1/test\"\u003euser1/test\u003c/a\u003e] user1 pushed 1 commit to \u003ca href=\"http://localhost:3000/user1/test/src/branch/master\"\u003emaster\u003c/a\u003e:\u003cbr\u003e\u003ca href=\"http://localhost:3000/user1/test/commit/5175ef26201c58b035a3404b3fe02b4e8d436eee\"\u003e5175ef2\u003c/a\u003e: 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", - "io.gitea.commits": [ - { - "id": "5175ef26201c58b035a3404b3fe02b4e8d436eee", - "message": "Merge pull request 'Change readme.md' (#2) from add-matrix-webhook into master\n\nReviewed-on: http://localhost:3000/user1/test/pulls/2\n", - "url": "http://localhost:3000/user1/test/commit/5175ef26201c58b035a3404b3fe02b4e8d436eee", - "author": { - "name": "user1", - "email": "user@mail.com", - "username": "" - }, - "committer": { - "name": "user1", - "email": "user@mail.com", - "username": "" - }, - "verification": null, - "timestamp": "0001-01-01T00:00:00Z", - "added": null, - "removed": null, - "modified": null - } - ] -}` - - req, err := getMatrixHookRequest(w, h) - require.NoError(t, err) - require.NotNil(t, req) - - 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 |