forgejo/services/webhook/matrix_test.go
oliverpool b6e81357bd
Add Webhook authorization header ()
_This is a different approach to , 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 )
- TeamCity 
- Gitea instances 
- 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 ), so that it gets available for all
present and future hook types (especially the custom ones ).

This would also solve the buggy matrix retry .

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>
2022-11-03 20:23:20 +02:00

245 lines
11 KiB
Go

// 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 webhook
import (
"testing"
webhook_model "code.gitea.io/gitea/models/webhook"
api "code.gitea.io/gitea/modules/structs"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestMatrixPayload(t *testing.T) {
t.Run("Create", func(t *testing.T) {
p := createTestPayload()
d := new(MatrixPayload)
pl, err := d.Create(p)
require.NoError(t, err)
require.NotNil(t, 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.(*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(MatrixPayload)
pl, err := d.Delete(p)
require.NoError(t, err)
require.NotNil(t, pl)
require.IsType(t, &MatrixPayload{}, pl)
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(MatrixPayload)
pl, err := d.Fork(p)
require.NoError(t, err)
require.NotNil(t, 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.(*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(MatrixPayload)
pl, err := d.Push(p)
require.NoError(t, err)
require.NotNil(t, 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.(*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(MatrixPayload)
p.Action = api.HookIssueOpened
pl, err := d.Issue(p)
require.NoError(t, err)
require.NotNil(t, 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.(*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, &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.(*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(MatrixPayload)
pl, err := d.IssueComment(p)
require.NoError(t, err)
require.NotNil(t, 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.(*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(MatrixPayload)
pl, err := d.PullRequest(p)
require.NoError(t, err)
require.NotNil(t, 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.(*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(MatrixPayload)
pl, err := d.IssueComment(p)
require.NoError(t, err)
require.NotNil(t, 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.(*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(MatrixPayload)
pl, err := d.Review(p, webhook_model.HookEventPullRequestReviewApproved)
require.NoError(t, err)
require.NotNil(t, 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.(*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(MatrixPayload)
pl, err := d.Repository(p)
require.NoError(t, err)
require.NotNil(t, 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.(*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(MatrixPayload)
p.Action = api.HookWikiCreated
pl, err := d.Wiki(p)
require.NoError(t, err)
require.NotNil(t, 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.(*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, &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.(*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, &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.(*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(MatrixPayload)
pl, err := d.Release(p)
require.NoError(t, err)
require.NotNil(t, 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.(*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(MatrixPayload).Push(p)
require.NoError(t, err)
require.NotNil(t, pl)
require.IsType(t, &MatrixPayload{}, pl)
json, err := pl.JSONPayload()
require.NoError(t, err)
assert.NotEmpty(t, json)
}
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)
})
}
}