diff --git a/models/webhook/webhook.go b/models/webhook/webhook.go index 4ab806573b..f3370f3db5 100644 --- a/models/webhook/webhook.go +++ b/models/webhook/webhook.go @@ -361,6 +361,15 @@ func (w Webhook) HeaderAuthorization() (string, error) { return secret.DecryptSecret(setting.SecretKey, w.HeaderAuthorizationEncrypted) } +// HeaderAuthorizationTrimPrefix returns the decrypted Authorization with a specified prefix trimmed. +func (w Webhook) HeaderAuthorizationTrimPrefix(prefix string) (string, error) { + s, err := w.HeaderAuthorization() + if err != nil { + return "", err + } + return strings.TrimPrefix(s, prefix), nil +} + // SetHeaderAuthorization encrypts and sets the Authorization header. func (w *Webhook) SetHeaderAuthorization(cleartext string) error { if cleartext == "" { diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 37fef6d227..79eb86a175 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -561,6 +561,7 @@ TeamName = Team name AuthName = Authorization name AdminEmail = Admin email To = Branch name +AccessToken = Access token NewBranchName = New branch name CommitSummary = Commit summary @@ -2378,6 +2379,7 @@ settings.slack_token = Token settings.slack_domain = Domain settings.slack_channel = Channel settings.add_web_hook_desc = Integrate %s into your repository. +settings.graphql_url = GraphQL URL settings.web_hook_name_gitea = Gitea settings.web_hook_name_forgejo = Forgejo settings.web_hook_name_gogs = Gogs @@ -2397,10 +2399,10 @@ settings.packagist_api_token = API token settings.packagist_package_url = Packagist package URL settings.web_hook_name_sourcehut_builds = SourceHut Builds settings.sourcehut_builds.manifest_path = Build manifest path -settings.sourcehut_builds.graphql_url = GraphQL URL (e.g. https://builds.sr.ht/query) settings.sourcehut_builds.visibility = Job visibility settings.sourcehut_builds.secrets = Secrets settings.sourcehut_builds.secrets_helper = Give the job access to the build secrets (requires the SECRETS:RO grant) +settings.sourcehut_builds.access_token_helper = Access token that has JOBS:RW grant. Generate a builds.sr.ht token or a builds.sr.ht token with secrets access on meta.sr.ht. settings.deploy_keys = Deploy keys settings.add_deploy_key = Add deploy key settings.deploy_key_desc = Deploy keys have read-only pull access to the repository. @@ -2507,6 +2509,8 @@ settings.thread_id = Thread ID settings.matrix.homeserver_url = Homeserver URL settings.matrix.room_id = Room ID settings.matrix.message_type = Message type +settings.matrix.access_token_helper = It is recommended to setup a dedicated Matrix account for this. The access token can be retrieved from the Element web client (in a private/incognito tab) > User menu (top left) > All settings > Help & About > Advanced > Access Token (right below the Homeserver URL). Close the private/incognito tab (logging out would invalidate the token). +settings.matrix.room_id_helper = The Room ID can be retrieved from the Element web client > Room Settings > Advanced > Internal room ID. Example: %s. settings.archive.button = Archive repo settings.archive.header = Archive this repo settings.archive.text = Archiving the repo will make it entirely read-only. It will be hidden from the dashboard. Nobody (not even you!) will be able to make new commits, or open any issues or pull requests. diff --git a/services/webhook/deliver.go b/services/webhook/deliver.go index d488b1725a..8db1c72572 100644 --- a/services/webhook/deliver.go +++ b/services/webhook/deliver.go @@ -78,7 +78,13 @@ func Deliver(ctx context.Context, t *webhook_model.HookTask) error { } if authorization != "" { req.Header.Set("Authorization", authorization) - t.RequestInfo.Headers["Authorization"] = "******" + redacted := "******" + if strings.HasPrefix(authorization, "Bearer ") { + redacted = "Bearer " + redacted + } else if strings.HasPrefix(authorization, "Basic ") { + redacted = "Basic " + redacted + } + t.RequestInfo.Headers["Authorization"] = redacted } t.ResponseInfo = &webhook_model.HookResponse{ diff --git a/services/webhook/deliver_test.go b/services/webhook/deliver_test.go index c689332666..0311d810e6 100644 --- a/services/webhook/deliver_test.go +++ b/services/webhook/deliver_test.go @@ -132,7 +132,7 @@ func TestWebhookDeliverAuthorizationHeader(t *testing.T) { } assert.True(t, hookTask.IsSucceed) - assert.Equal(t, "******", hookTask.RequestInfo.Headers["Authorization"]) + assert.Equal(t, "Bearer ******", hookTask.RequestInfo.Headers["Authorization"]) } func TestWebhookDeliverHookTask(t *testing.T) { @@ -152,7 +152,6 @@ func TestWebhookDeliverHookTask(t *testing.T) { case "/webhook/6db5dc1e282529a8c162c7fe93dd2667494eeb51": // Version 2 - assert.Equal(t, "push", r.Header.Get("X-GitHub-Event")) assert.Equal(t, "application/json", r.Header.Get("Content-Type")) body, err := io.ReadAll(r.Body) assert.NoError(t, err) diff --git a/services/webhook/matrix.go b/services/webhook/matrix.go index 697e33e94c..24d6cc6d99 100644 --- a/services/webhook/matrix.go +++ b/services/webhook/matrix.go @@ -42,16 +42,15 @@ func (matrixHandler) UnmarshalForm(bind func(any)) forms.WebhookForm { HomeserverURL string `binding:"Required;ValidUrl"` RoomID string `binding:"Required"` MessageType int - - // enforce requirement of authorization_header - // (value will still be set in the embedded WebhookCoreForm) - AuthorizationHeader string `binding:"Required"` + AccessToken string `binding:"Required"` } bind(&form) + form.AuthorizationHeader = "Bearer " + strings.TrimSpace(form.AccessToken) + // https://spec.matrix.org/v1.10/client-server-api/#sending-events-to-a-room return forms.WebhookForm{ WebhookCoreForm: form.WebhookCoreForm, - URL: fmt.Sprintf("%s/_matrix/client/r0/rooms/%s/send/m.room.message", form.HomeserverURL, url.PathEscape(form.RoomID)), + URL: fmt.Sprintf("%s/_matrix/client/v3/rooms/%s/send/m.room.message", form.HomeserverURL, url.PathEscape(form.RoomID)), ContentType: webhook_model.ContentTypeJSON, Secret: "", HTTPMethod: http.MethodPut, @@ -91,7 +90,7 @@ func (matrixHandler) NewRequest(ctx context.Context, w *webhook_model.Webhook, t } req.Header.Set("Content-Type", "application/json") - return req, body, shared.AddDefaultHeaders(req, []byte(w.Secret), t, body) // likely useless, but has always been sent historially + return req, body, nil } const matrixPayloadSizeLimit = 1024 * 64 diff --git a/services/webhook/matrix_test.go b/services/webhook/matrix_test.go index 0269ccccea..7031a45bec 100644 --- a/services/webhook/matrix_test.go +++ b/services/webhook/matrix_test.go @@ -201,7 +201,7 @@ func TestMatrixJSONPayload(t *testing.T) { RepoID: 3, IsActive: true, Type: webhook_module.MATRIX, - URL: "https://matrix.example.com/_matrix/client/r0/rooms/ROOM_ID/send/m.room.message", + URL: "https://matrix.example.com/_matrix/client/v3/rooms/ROOM_ID/send/m.room.message", Meta: `{"message_type":0}`, // text } task := &webhook_model.HookTask{ @@ -217,8 +217,7 @@ func TestMatrixJSONPayload(t *testing.T) { require.NoError(t, err) assert.Equal(t, "PUT", req.Method) - assert.Equal(t, "/_matrix/client/r0/rooms/ROOM_ID/send/m.room.message/6db5dc1e282529a8c162c7fe93dd2667494eeb51", req.URL.Path) - assert.Equal(t, "sha256=", req.Header.Get("X-Hub-Signature-256")) + assert.Equal(t, "/_matrix/client/v3/rooms/ROOM_ID/send/m.room.message/6db5dc1e282529a8c162c7fe93dd2667494eeb51", req.URL.Path) assert.Equal(t, "application/json", req.Header.Get("Content-Type")) var body MatrixPayload err = json.NewDecoder(req.Body).Decode(&body) diff --git a/services/webhook/sourcehut/builds.go b/services/webhook/sourcehut/builds.go index 1561b9e6e6..e7501110a2 100644 --- a/services/webhook/sourcehut/builds.go +++ b/services/webhook/sourcehut/builds.go @@ -49,6 +49,7 @@ type buildsForm struct { ManifestPath string `binding:"Required"` Visibility string `binding:"Required;In(PUBLIC,UNLISTED,PRIVATE)"` Secrets bool + AccessToken string `binding:"Required"` } var _ binding.Validator = &buildsForm{} @@ -63,13 +64,7 @@ func (f *buildsForm) Validate(req *http.Request, errs binding.Errors) binding.Er Message: ctx.Locale.TrString("repo.settings.add_webhook.invalid_path"), }) } - if !strings.HasPrefix(f.AuthorizationHeader, "Bearer ") { - errs = append(errs, binding.Error{ - FieldNames: []string{"AuthorizationHeader"}, - Classification: "", - Message: ctx.Locale.TrString("form.required_prefix", "Bearer "), - }) - } + f.AuthorizationHeader = "Bearer " + strings.TrimSpace(f.AccessToken) return errs } diff --git a/templates/webhook/new/dingtalk.tmpl b/templates/webhook/new/dingtalk.tmpl index 398cb45f8c..e805eadc04 100644 --- a/templates/webhook/new/dingtalk.tmpl +++ b/templates/webhook/new/dingtalk.tmpl @@ -5,5 +5,5 @@ - {{template "repo/settings/webhook/settings" .}} + {{template "webhook/shared-settings" .}} diff --git a/templates/webhook/new/discord.tmpl b/templates/webhook/new/discord.tmpl index c6a7ea7d19..48ac84fc6b 100644 --- a/templates/webhook/new/discord.tmpl +++ b/templates/webhook/new/discord.tmpl @@ -13,5 +13,5 @@ - {{template "repo/settings/webhook/settings" .}} + {{template "webhook/shared-settings" .}} diff --git a/templates/webhook/new/feishu.tmpl b/templates/webhook/new/feishu.tmpl index ead11770de..10c2827d65 100644 --- a/templates/webhook/new/feishu.tmpl +++ b/templates/webhook/new/feishu.tmpl @@ -6,5 +6,5 @@ - {{template "repo/settings/webhook/settings" .}} + {{template "webhook/shared-settings" .}} diff --git a/templates/webhook/new/forgejo.tmpl b/templates/webhook/new/forgejo.tmpl index 6bc4eb489e..cdb3334690 100644 --- a/templates/webhook/new/forgejo.tmpl +++ b/templates/webhook/new/forgejo.tmpl @@ -34,5 +34,5 @@ - {{template "repo/settings/webhook/settings" .}} + {{template "webhook/shared-settings" .}} diff --git a/templates/webhook/new/gitea.tmpl b/templates/webhook/new/gitea.tmpl index a8e66202dd..3926370be2 100644 --- a/templates/webhook/new/gitea.tmpl +++ b/templates/webhook/new/gitea.tmpl @@ -34,5 +34,5 @@ - {{template "repo/settings/webhook/settings" .}} + {{template "webhook/shared-settings" .}} diff --git a/templates/webhook/new/gogs.tmpl b/templates/webhook/new/gogs.tmpl index 876c0e2be6..5b6cd7235c 100644 --- a/templates/webhook/new/gogs.tmpl +++ b/templates/webhook/new/gogs.tmpl @@ -22,5 +22,5 @@ - {{template "repo/settings/webhook/settings" .}} + {{template "webhook/shared-settings" .}} diff --git a/templates/webhook/new/matrix.tmpl b/templates/webhook/new/matrix.tmpl index afa28941bf..920180d4c8 100644 --- a/templates/webhook/new/matrix.tmpl +++ b/templates/webhook/new/matrix.tmpl @@ -5,9 +5,16 @@ + +
+ + + {{ctx.Locale.Tr "repo.settings.matrix.access_token_helper"}} +
- + + {{ctx.Locale.Tr "repo.settings.matrix.room_id_helper" ("!opaque_id:example.org"|SafeHTML)}}
@@ -21,5 +28,5 @@
- {{template "repo/settings/webhook/settings" .}} + {{template "webhook/shared-settings" .}} diff --git a/templates/webhook/new/msteams.tmpl b/templates/webhook/new/msteams.tmpl index f668342ac8..535d0fc3f1 100644 --- a/templates/webhook/new/msteams.tmpl +++ b/templates/webhook/new/msteams.tmpl @@ -5,5 +5,5 @@ - {{template "repo/settings/webhook/settings" .}} + {{template "webhook/shared-settings" .}} diff --git a/templates/webhook/new/packagist.tmpl b/templates/webhook/new/packagist.tmpl index 5cf6ed0012..04240bbf93 100644 --- a/templates/webhook/new/packagist.tmpl +++ b/templates/webhook/new/packagist.tmpl @@ -13,5 +13,5 @@ - {{template "repo/settings/webhook/settings" .}} + {{template "webhook/shared-settings" .}} diff --git a/templates/webhook/new/slack.tmpl b/templates/webhook/new/slack.tmpl index d24e659097..cfaeb41f20 100644 --- a/templates/webhook/new/slack.tmpl +++ b/templates/webhook/new/slack.tmpl @@ -22,5 +22,5 @@ - {{template "repo/settings/webhook/settings" .}} + {{template "webhook/shared-settings" .}} diff --git a/templates/webhook/new/sourcehut_builds.tmpl b/templates/webhook/new/sourcehut_builds.tmpl index 1d6333fe79..3bcbe1bf6e 100644 --- a/templates/webhook/new/sourcehut_builds.tmpl +++ b/templates/webhook/new/sourcehut_builds.tmpl @@ -2,12 +2,12 @@
{{.CsrfTokenHtml}}
- - + +
- +
@@ -29,5 +29,11 @@ {{ctx.Locale.Tr "repo.settings.sourcehut_builds.secrets_helper"}}
- {{template "repo/settings/webhook/settings" .}} + +
+ + + {{ctx.Locale.Tr "repo.settings.sourcehut_builds.access_token_helper" "https://meta.sr.ht/oauth2/personal-token?grants=builds.sr.ht/JOBS:RW" "https://meta.sr.ht/oauth2/personal-token?grants=builds.sr.ht/JOBS:RW+builds.sr.ht/SECRETS:RO"}} +
+ {{template "webhook/shared-settings" .}}
diff --git a/templates/webhook/new/telegram.tmpl b/templates/webhook/new/telegram.tmpl index ea677ca4c3..3627dff36a 100644 --- a/templates/webhook/new/telegram.tmpl +++ b/templates/webhook/new/telegram.tmpl @@ -13,5 +13,5 @@ - {{template "repo/settings/webhook/settings" .}} + {{template "webhook/shared-settings" .}} diff --git a/templates/webhook/new/wechatwork.tmpl b/templates/webhook/new/wechatwork.tmpl index 15050d71bd..ae9d36a7a6 100644 --- a/templates/webhook/new/wechatwork.tmpl +++ b/templates/webhook/new/wechatwork.tmpl @@ -5,5 +5,5 @@ - {{template "repo/settings/webhook/settings" .}} + {{template "webhook/shared-settings" .}} diff --git a/templates/repo/settings/webhook/settings.tmpl b/templates/webhook/shared-settings.tmpl similarity index 94% rename from templates/repo/settings/webhook/settings.tmpl rename to templates/webhook/shared-settings.tmpl index 0a39643260..80caad7279 100644 --- a/templates/repo/settings/webhook/settings.tmpl +++ b/templates/webhook/shared-settings.tmpl @@ -258,14 +258,15 @@ {{ctx.Locale.Tr "repo.settings.branch_filter_desc"}} - -
- - - {{if ne .HookType "matrix"}}{{/* Matrix doesn't make the authorization optional but it is implied by the help string, should be changed.*/}} +{{$skipAuthorizationHeader := or (eq .HookType "sourcehut_builds") (eq .HookType "matrix")}} +{{if not $skipAuthorizationHeader}} + +
+ + {{ctx.Locale.Tr "repo.settings.authorization_header_desc" ("Bearer token123456, Basic YWxhZGRpbjpvcGVuc2VzYW1l" | SafeHTML)}} - {{end}} -
+
+{{end}}
diff --git a/tests/integration/repo_webhook_test.go b/tests/integration/repo_webhook_test.go index 9a278e706d..b98fce0f4a 100644 --- a/tests/integration/repo_webhook_test.go +++ b/tests/integration/repo_webhook_test.go @@ -227,19 +227,19 @@ func TestWebhookForms(t *testing.T) { })) t.Run("matrix/required", testWebhookForms("matrix", session, map[string]string{ - "homeserver_url": "https://matrix.example.com", - "room_id": "123", - "authorization_header": "Bearer 123456", + "homeserver_url": "https://matrix.example.com", + "access_token": "123456", + "room_id": "123", }, map[string]string{ - "authorization_header": "", + "access_token": "", })) t.Run("matrix/optional", testWebhookForms("matrix", session, map[string]string{ "homeserver_url": "https://matrix.example.com", + "access_token": "123456", "room_id": "123", "message_type": "1", // m.text - "branch_filter": "matrix/*", - "authorization_header": "Bearer 123456", + "branch_filter": "matrix/*", })) t.Run("wechatwork/required", testWebhookForms("wechatwork", session, map[string]string{ @@ -267,14 +267,12 @@ func TestWebhookForms(t *testing.T) { })) t.Run("sourcehut_builds/required", testWebhookForms("sourcehut_builds", session, map[string]string{ - "payload_url": "https://sourcehut_builds.example.com", - "manifest_path": ".build.yml", - "visibility": "PRIVATE", - "authorization_header": "Bearer 123456", + "payload_url": "https://sourcehut_builds.example.com", + "manifest_path": ".build.yml", + "visibility": "PRIVATE", + "access_token": "123456", }, map[string]string{ - "authorization_header": "", - }, map[string]string{ - "authorization_header": "token ", + "access_token": "", }, map[string]string{ "manifest_path": "", }, map[string]string{ @@ -289,9 +287,9 @@ func TestWebhookForms(t *testing.T) { "manifest_path": ".build.yml", "visibility": "PRIVATE", "secrets": "on", + "access_token": "123456", - "branch_filter": "srht/*", - "authorization_header": "Bearer 123456", + "branch_filter": "srht/*", })) }