From 1fdababf7c519722280ff2cd43eefece70fc2158 Mon Sep 17 00:00:00 2001
From: justusbunsi <61625851+justusbunsi@users.noreply.github.com>
Date: Sat, 2 Jul 2022 19:13:13 +0200
Subject: [PATCH 01/14] Implement frontend part
Signed-off-by: justusbunsi <61625851+justusbunsi@users.noreply.github.com>
---
options/locale/locale_en-US.ini | 9 ++++
templates/repo/settings/webhook/gitea.tmpl | 44 ++++++++++++++++
web_src/js/features/comp/WebHookEditor.js | 58 ++++++++++++++++++++++
3 files changed, 111 insertions(+)
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index eb7ae4774313b..47ea245e55732 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -1922,6 +1922,15 @@ settings.webhook.payload = Content
settings.webhook.body = Body
settings.webhook.replay.description = Replay this webhook.
settings.webhook.delivery.success = An event has been added to the delivery queue. It may take few seconds before it shows up in the delivery history.
+settings.webhook.auth_header.section = Authorization Header
+settings.webhook.auth_header.description = Add authorization header to webhook delivery.
+settings.webhook.auth_header.name = Header name
+settings.webhook.auth_header.type = Header content type
+settings.webhook.auth_header.type_basic = Basic authorization
+settings.webhook.auth_header.type_token = Token authorization
+settings.webhook.auth_header.username = Username
+settings.webhook.auth_header.password = Password
+settings.webhook.auth_header.token = Token
settings.githooks_desc = "Git Hooks are powered by Git itself. You can edit hook files below to set up custom operations."
settings.githook_edit_desc = If the hook is inactive, sample content will be presented. Leaving content to an empty value will disable this hook.
settings.githook_name = Hook Name
diff --git a/templates/repo/settings/webhook/gitea.tmpl b/templates/repo/settings/webhook/gitea.tmpl
index 062948b6abefd..703af4030a7e9 100644
--- a/templates/repo/settings/webhook/gitea.tmpl
+++ b/templates/repo/settings/webhook/gitea.tmpl
@@ -35,6 +35,50 @@
{{.locale.Tr "repo.settings.secret"}}
+
+
+
+
+
+
+
{{template "repo/settings/webhook/settings" .}}
{{end}}
diff --git a/web_src/js/features/comp/WebHookEditor.js b/web_src/js/features/comp/WebHookEditor.js
index 85a4f92809fa1..7cce08a1445d4 100644
--- a/web_src/js/features/comp/WebHookEditor.js
+++ b/web_src/js/features/comp/WebHookEditor.js
@@ -1,11 +1,69 @@
import $ from 'jquery';
const {csrfToken} = window.config;
+const initAuthenticationHeaderSection = function() {
+ const $authHeaderSection = $('.auth-headers');
+
+ if ($authHeaderSection.length === 0) {
+ return;
+ }
+
+ const $checkbox = $authHeaderSection.find('.checkbox input');
+
+ const updateHeaderContentType = function() {
+ const isBasicAuth = $authHeaderSection.find('#auth_header_type').val() === 'basic';
+ const $basicAuthFields = $authHeaderSection.find('.basic-auth');
+ const $tokenAuthFields = $authHeaderSection.find('.token-auth');
+
+ if (isBasicAuth) {
+ $basicAuthFields.addClass("required");
+ $basicAuthFields.find('input').attr("required", "");
+ $basicAuthFields.show();
+
+ $tokenAuthFields.removeClass("required");
+ $tokenAuthFields.find('input').removeAttr("required");
+ $tokenAuthFields.hide();
+ } else {
+ $basicAuthFields.removeClass("required");
+ $basicAuthFields.find('input').removeAttr("required");
+ $basicAuthFields.hide();
+
+ $tokenAuthFields.addClass("required");
+ $tokenAuthFields.find('input').attr("required", "");
+ $tokenAuthFields.show();
+ }
+ };
+
+ const updateHeaderCheckbox = function() {
+ if ($checkbox.is(':checked')) {
+ const $headerName = $authHeaderSection.find('#auth_header_name');
+ $headerName.attr("required", "");
+ $headerName.parent().addClass("required");
+ $headerName.parent().show();
+ $authHeaderSection.find('#auth_header_type').parent().parent().show();
+ updateHeaderContentType();
+ } else {
+ $authHeaderSection.find('.auth-header').hide();
+ $authHeaderSection.find('.auth-header').removeClass("required");
+ $authHeaderSection.find('.auth-header input').removeAttr("required");
+ }
+ };
+
+ if ($checkbox.is(':checked')) {
+ updateHeaderCheckbox();
+ }
+
+ $checkbox.on('change', updateHeaderCheckbox);
+ $authHeaderSection.find('#auth_header_type').on('change', updateHeaderContentType);
+};
+
export function initCompWebHookEditor() {
if ($('.new.webhook').length === 0) {
return;
}
+ initAuthenticationHeaderSection();
+
$('.events.checkbox input').on('change', function () {
if ($(this).is(':checked')) {
$('.events.fields').show();
From 08b43177e5c3a3c8c329e8532711956551e96a90 Mon Sep 17 00:00:00 2001
From: justusbunsi <61625851+justusbunsi@users.noreply.github.com>
Date: Sun, 3 Jul 2022 13:55:30 +0200
Subject: [PATCH 02/14] Persist auth header configuration
Signed-off-by: justusbunsi <61625851+justusbunsi@users.noreply.github.com>
---
routers/web/repo/webhook.go | 47 ++++++++++++++++++++++
services/forms/repo_form.go | 14 +++++--
services/webhook/gitea.go | 44 ++++++++++++++++++++
templates/repo/settings/webhook/gitea.tmpl | 12 +++---
4 files changed, 107 insertions(+), 10 deletions(-)
create mode 100644 services/webhook/gitea.go
diff --git a/routers/web/repo/webhook.go b/routers/web/repo/webhook.go
index a9b14ee21f453..a81909736bb0a 100644
--- a/routers/web/repo/webhook.go
+++ b/routers/web/repo/webhook.go
@@ -21,6 +21,7 @@ import (
"code.gitea.io/gitea/modules/convert"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/json"
+ "code.gitea.io/gitea/modules/secret"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
@@ -211,6 +212,27 @@ func GiteaHooksNewPost(ctx *context.Context) {
contentType = webhook.ContentTypeForm
}
+ meta, err := json.Marshal(&webhook_service.GiteaMeta{
+ AuthHeader: webhook_service.GiteaAuthHeaderMeta{
+ Active: form.AuthHeaderActive,
+ Name: form.AuthHeaderName,
+ Type: form.AuthHeaderType,
+ Username: form.AuthHeaderUsername,
+ Password: form.AuthHeaderPassword,
+ Token: form.AuthHeaderToken,
+ },
+ })
+ if err != nil {
+ ctx.ServerError("Marshal", err)
+ return
+ }
+
+ encryptedMeta, err := secret.EncryptSecret(setting.SecretKey, string(meta))
+ if err != nil {
+ ctx.ServerError("Encrypt", err)
+ return
+ }
+
w := &webhook.Webhook{
RepoID: orCtx.RepoID,
URL: form.PayloadURL,
@@ -220,6 +242,7 @@ func GiteaHooksNewPost(ctx *context.Context) {
HookEvent: ParseHookEvent(form.WebhookForm),
IsActive: form.Active,
Type: webhook.GITEA,
+ Meta: encryptedMeta,
OrgID: orCtx.OrgID,
IsSystemWebhook: orCtx.IsSystemWebhook,
}
@@ -761,6 +784,8 @@ func checkWebhook(ctx *context.Context) (*orgRepoCtx, *webhook.Webhook) {
ctx.Data["HookType"] = w.Type
switch w.Type {
+ case webhook.GITEA:
+ ctx.Data["GiteaHook"] = webhook_service.GetGiteaHook(w)
case webhook.SLACK:
ctx.Data["SlackHook"] = webhook_service.GetSlackHook(w)
case webhook.DISCORD:
@@ -818,10 +843,32 @@ func WebHooksEditPost(ctx *context.Context) {
contentType = webhook.ContentTypeForm
}
+ meta, err := json.Marshal(&webhook_service.GiteaMeta{
+ AuthHeader: webhook_service.GiteaAuthHeaderMeta{
+ Active: form.AuthHeaderActive,
+ Name: form.AuthHeaderName,
+ Type: form.AuthHeaderType,
+ Username: form.AuthHeaderUsername,
+ Password: form.AuthHeaderPassword,
+ Token: form.AuthHeaderToken,
+ },
+ })
+ if err != nil {
+ ctx.ServerError("Marshal", err)
+ return
+ }
+
+ encryptedMeta, err := secret.EncryptSecret(setting.SecretKey, string(meta))
+ if err != nil {
+ ctx.ServerError("Encrypt", err)
+ return
+ }
+
w.URL = form.PayloadURL
w.ContentType = contentType
w.Secret = form.Secret
w.HookEvent = ParseHookEvent(form.WebhookForm)
+ w.Meta = encryptedMeta
w.IsActive = form.Active
w.HTTPMethod = form.HTTPMethod
if err := w.UpdateEvent(); err != nil {
diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go
index c9327bbd9b0f8..1703620af210b 100644
--- a/services/forms/repo_form.go
+++ b/services/forms/repo_form.go
@@ -264,10 +264,16 @@ func (f WebhookForm) ChooseEvents() bool {
// NewWebhookForm form for creating web hook
type NewWebhookForm struct {
- PayloadURL string `binding:"Required;ValidUrl"`
- HTTPMethod string `binding:"Required;In(POST,GET)"`
- ContentType int `binding:"Required"`
- Secret string
+ PayloadURL string `binding:"Required;ValidUrl"`
+ HTTPMethod string `binding:"Required;In(POST,GET)"`
+ ContentType int `binding:"Required"`
+ Secret string
+ AuthHeaderActive bool
+ AuthHeaderName string
+ AuthHeaderType string `binding:"In(basic,token)"`
+ AuthHeaderUsername string
+ AuthHeaderPassword string
+ AuthHeaderToken string
WebhookForm
}
diff --git a/services/webhook/gitea.go b/services/webhook/gitea.go
new file mode 100644
index 0000000000000..2e26c92cdd602
--- /dev/null
+++ b/services/webhook/gitea.go
@@ -0,0 +1,44 @@
+// Copyright 2022 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 (
+ webhook_model "code.gitea.io/gitea/models/webhook"
+ "code.gitea.io/gitea/modules/json"
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/secret"
+ "code.gitea.io/gitea/modules/setting"
+)
+
+type (
+ // GiteaAuthHeaderMeta contains the authentication header metadata
+ GiteaAuthHeaderMeta struct {
+ Active bool `json:"active"`
+ Name string `json:"name"`
+ Type string `json:"type"`
+ Username string `json:"username,omitempty"`
+ Password string `json:"password,omitempty"`
+ Token string `json:"token,omitempty"`
+ }
+
+ // GiteaMeta contains the gitea webhook metadata
+ GiteaMeta struct {
+ AuthHeader GiteaAuthHeaderMeta `json:"authHeader"`
+ }
+)
+
+// GetGiteaHook returns gitea metadata
+func GetGiteaHook(w *webhook_model.Webhook) *GiteaMeta {
+ meta, err := secret.DecryptSecret(setting.SecretKey, w.Meta)
+ if err != nil {
+ log.Error("webhook.GetGiteaHook(%d): %v", w.ID, err)
+ }
+
+ s := &GiteaMeta{}
+ if err := json.Unmarshal([]byte(meta), s); err != nil {
+ log.Error("webhook.GetGiteaHook(%d): %v", w.ID, err)
+ }
+ return s
+}
diff --git a/templates/repo/settings/webhook/gitea.tmpl b/templates/repo/settings/webhook/gitea.tmpl
index 703af4030a7e9..a4c8302ac5397 100644
--- a/templates/repo/settings/webhook/gitea.tmpl
+++ b/templates/repo/settings/webhook/gitea.tmpl
@@ -42,19 +42,19 @@
{{.locale.Tr "repo.settings.webhook.auth_header.section"}}
From fcfc79f8f48cc39f821e065722fc4f776d0054ad Mon Sep 17 00:00:00 2001
From: justusbunsi <61625851+justusbunsi@users.noreply.github.com>
Date: Sun, 3 Jul 2022 19:05:11 +0200
Subject: [PATCH 07/14] Apply code formatting
Signed-off-by: justusbunsi <61625851+justusbunsi@users.noreply.github.com>
---
services/webhook/gitea.go | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/services/webhook/gitea.go b/services/webhook/gitea.go
index 16727e5ec2d19..2417408ea4f21 100644
--- a/services/webhook/gitea.go
+++ b/services/webhook/gitea.go
@@ -25,8 +25,8 @@ type (
// GiteaMeta contains the gitea webhook metadata
GiteaMeta struct {
- AuthHeaderEnabled bool `json:"auth_header_enabled"`
- AuthHeaderData string `json:"auth_header,omitempty"`
+ AuthHeaderEnabled bool `json:"auth_header_enabled"`
+ AuthHeaderData string `json:"auth_header,omitempty"`
AuthHeader GiteaAuthHeaderMeta `json:"-"`
}
)
From 8e210e674a72aa8f0170b838b229b049c857b557 Mon Sep 17 00:00:00 2001
From: justusbunsi <61625851+justusbunsi@users.noreply.github.com>
Date: Mon, 4 Jul 2022 16:53:22 +0200
Subject: [PATCH 08/14] Pass encrypt and decrypt functions as parameters
Signed-off-by: justusbunsi <61625851+justusbunsi@users.noreply.github.com>
---
modules/secret/secret.go | 5 +++++
routers/web/repo/webhook.go | 7 ++++---
services/webhook/deliver.go | 3 ++-
services/webhook/gitea.go | 8 ++++----
4 files changed, 15 insertions(+), 8 deletions(-)
diff --git a/modules/secret/secret.go b/modules/secret/secret.go
index e7edc7a95e528..3ef1b30c994c3 100644
--- a/modules/secret/secret.go
+++ b/modules/secret/secret.go
@@ -15,6 +15,11 @@ import (
"io"
)
+type (
+ EncryptSecretCallable func(key, str string) (string, error)
+ DecryptSecretCallable func(key, cipherhex string) (string, error)
+)
+
// AesEncrypt encrypts text and given key with AES.
func AesEncrypt(key, text []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
diff --git a/routers/web/repo/webhook.go b/routers/web/repo/webhook.go
index 651eeb3e26f5c..e8af050eb4d0d 100644
--- a/routers/web/repo/webhook.go
+++ b/routers/web/repo/webhook.go
@@ -21,6 +21,7 @@ import (
"code.gitea.io/gitea/modules/convert"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/json"
+ "code.gitea.io/gitea/modules/secret"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
@@ -211,7 +212,7 @@ func GiteaHooksNewPost(ctx *context.Context) {
contentType = webhook.ContentTypeForm
}
- meta, err := webhook_service.CreateGiteaHook(form)
+ meta, err := webhook_service.CreateGiteaHook(form, secret.EncryptSecret)
if err != nil {
ctx.ServerError("Meta", err)
return
@@ -769,7 +770,7 @@ func checkWebhook(ctx *context.Context) (*orgRepoCtx, *webhook.Webhook) {
ctx.Data["HookType"] = w.Type
switch w.Type {
case webhook.GITEA:
- ctx.Data["GiteaHook"] = webhook_service.GetGiteaHook(w)
+ ctx.Data["GiteaHook"] = webhook_service.GetGiteaHook(w, secret.DecryptSecret)
case webhook.SLACK:
ctx.Data["SlackHook"] = webhook_service.GetSlackHook(w)
case webhook.DISCORD:
@@ -827,7 +828,7 @@ func WebHooksEditPost(ctx *context.Context) {
contentType = webhook.ContentTypeForm
}
- meta, err := webhook_service.CreateGiteaHook(form)
+ meta, err := webhook_service.CreateGiteaHook(form, secret.EncryptSecret)
if err != nil {
ctx.ServerError("Meta", err)
return
diff --git a/services/webhook/deliver.go b/services/webhook/deliver.go
index cfbea0fd42c7f..d0ccb4251d85b 100644
--- a/services/webhook/deliver.go
+++ b/services/webhook/deliver.go
@@ -27,6 +27,7 @@ import (
"code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/proxy"
"code.gitea.io/gitea/modules/queue"
+ "code.gitea.io/gitea/modules/secret"
"code.gitea.io/gitea/modules/setting"
"github.com/gobwas/glob"
@@ -147,7 +148,7 @@ func Deliver(ctx context.Context, t *webhook_model.HookTask) error {
}
if w.Type == webhook_model.GITEA {
- meta := GetGiteaHook(w)
+ meta := GetGiteaHook(w, secret.DecryptSecret)
if meta.AuthHeaderEnabled {
var content string
switch meta.AuthHeader.Type {
diff --git a/services/webhook/gitea.go b/services/webhook/gitea.go
index 2417408ea4f21..606a79e73cac1 100644
--- a/services/webhook/gitea.go
+++ b/services/webhook/gitea.go
@@ -32,7 +32,7 @@ type (
)
// GetGiteaHook returns decrypted gitea metadata
-func GetGiteaHook(w *webhook_model.Webhook) *GiteaMeta {
+func GetGiteaHook(w *webhook_model.Webhook, decryptFn secret.DecryptSecretCallable) *GiteaMeta {
s := &GiteaMeta{}
// Legacy webhook configuration has no stored metadata
@@ -48,7 +48,7 @@ func GetGiteaHook(w *webhook_model.Webhook) *GiteaMeta {
return s
}
- headerData, err := secret.DecryptSecret(setting.SecretKey, s.AuthHeaderData)
+ headerData, err := decryptFn(setting.SecretKey, s.AuthHeaderData)
if err != nil {
log.Error("webhook.GetGiteaHook(%d): %v", w.ID, err)
}
@@ -67,7 +67,7 @@ func GetGiteaHook(w *webhook_model.Webhook) *GiteaMeta {
// CreateGiteaHook creates an gitea metadata string with encrypted auth header data,
// while it ensures to store the least necessary data in the database.
-func CreateGiteaHook(form *forms.NewWebhookForm) (string, error) {
+func CreateGiteaHook(form *forms.NewWebhookForm, encryptFn secret.EncryptSecretCallable) (string, error) {
metaObject := &GiteaMeta{
AuthHeaderEnabled: form.AuthHeaderActive,
}
@@ -91,7 +91,7 @@ func CreateGiteaHook(form *forms.NewWebhookForm) (string, error) {
return "", err
}
- encryptedHeaderData, err := secret.EncryptSecret(setting.SecretKey, string(headerData))
+ encryptedHeaderData, err := encryptFn(setting.SecretKey, string(headerData))
if err != nil {
return "", err
}
From 88b734e56f9db1d21062645efdb261195371fb32 Mon Sep 17 00:00:00 2001
From: justusbunsi <61625851+justusbunsi@users.noreply.github.com>
Date: Mon, 4 Jul 2022 19:08:22 +0200
Subject: [PATCH 09/14] Add early exit on error
Signed-off-by: justusbunsi <61625851+justusbunsi@users.noreply.github.com>
---
services/webhook/gitea.go | 3 +++
1 file changed, 3 insertions(+)
diff --git a/services/webhook/gitea.go b/services/webhook/gitea.go
index 606a79e73cac1..176b6a9c80824 100644
--- a/services/webhook/gitea.go
+++ b/services/webhook/gitea.go
@@ -42,6 +42,7 @@ func GetGiteaHook(w *webhook_model.Webhook, decryptFn secret.DecryptSecretCallab
if err := json.Unmarshal([]byte(w.Meta), s); err != nil {
log.Error("webhook.GetGiteaHook(%d): %v", w.ID, err)
+ return nil
}
if !s.AuthHeaderEnabled {
@@ -51,11 +52,13 @@ func GetGiteaHook(w *webhook_model.Webhook, decryptFn secret.DecryptSecretCallab
headerData, err := decryptFn(setting.SecretKey, s.AuthHeaderData)
if err != nil {
log.Error("webhook.GetGiteaHook(%d): %v", w.ID, err)
+ return nil
}
h := GiteaAuthHeaderMeta{}
if err := json.Unmarshal([]byte(headerData), &h); err != nil {
log.Error("webhook.GetGiteaHook(%d): %v", w.ID, err)
+ return nil
}
// Replace encrypted content with decrypted settings
From 3f33b2df3d68ab3967460719c385d77ad033c1d1 Mon Sep 17 00:00:00 2001
From: justusbunsi <61625851+justusbunsi@users.noreply.github.com>
Date: Mon, 4 Jul 2022 19:30:36 +0200
Subject: [PATCH 10/14] Add GiteaHook unit tests
Signed-off-by: justusbunsi <61625851+justusbunsi@users.noreply.github.com>
---
services/webhook/gitea.go | 2 +-
services/webhook/gitea_test.go | 208 +++++++++++++++++++++++++++++++++
2 files changed, 209 insertions(+), 1 deletion(-)
create mode 100644 services/webhook/gitea_test.go
diff --git a/services/webhook/gitea.go b/services/webhook/gitea.go
index 176b6a9c80824..827e9d85aaf91 100644
--- a/services/webhook/gitea.go
+++ b/services/webhook/gitea.go
@@ -68,7 +68,7 @@ func GetGiteaHook(w *webhook_model.Webhook, decryptFn secret.DecryptSecretCallab
return s
}
-// CreateGiteaHook creates an gitea metadata string with encrypted auth header data,
+// CreateGiteaHook creates a gitea metadata string with encrypted auth header data,
// while it ensures to store the least necessary data in the database.
func CreateGiteaHook(form *forms.NewWebhookForm, encryptFn secret.EncryptSecretCallable) (string, error) {
metaObject := &GiteaMeta{
diff --git a/services/webhook/gitea_test.go b/services/webhook/gitea_test.go
new file mode 100644
index 0000000000000..1f0445db9189b
--- /dev/null
+++ b/services/webhook/gitea_test.go
@@ -0,0 +1,208 @@
+// Copyright 2022 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 (
+ "fmt"
+ "testing"
+
+ webhook_model "code.gitea.io/gitea/models/webhook"
+ "code.gitea.io/gitea/services/forms"
+
+ "github.com/stretchr/testify/require"
+)
+
+type GiteaSecretModuleMock struct {
+ DecryptCalled bool
+ EncryptCalled bool
+ SimulateError bool
+}
+
+func (m *GiteaSecretModuleMock) DecryptSecret(key string, cipherhex string) (string, error) {
+ m.DecryptCalled = true
+
+ if m.SimulateError {
+ return "", fmt.Errorf("Simulated error")
+ }
+
+ return cipherhex, nil
+}
+
+func (m *GiteaSecretModuleMock) EncryptSecret(key, str string) (string, error) {
+ m.EncryptCalled = true
+
+ if m.SimulateError {
+ return "", fmt.Errorf("Simulated error")
+ }
+
+ return str, nil
+}
+
+func TestGetGiteaHook(t *testing.T) {
+ t.Run("Legacy configuration", func(t *testing.T) {
+ s := &webhook_model.Webhook{
+ Type: webhook_model.GITEA,
+ Meta: "",
+ }
+
+ m := GiteaSecretModuleMock{}
+
+ actual := GetGiteaHook(s, m.DecryptSecret)
+
+ require.NotNil(t, actual)
+ require.IsType(t, &GiteaMeta{}, actual)
+ require.False(t, actual.AuthHeaderEnabled)
+ require.False(t, m.DecryptCalled, "Decrypt function unexpectedly called")
+ })
+
+ t.Run("Disabled auth headers", func(t *testing.T) {
+ s := &webhook_model.Webhook{
+ Type: webhook_model.GITEA,
+ Meta: `{"auth_header_enabled": false}`,
+ }
+
+ m := GiteaSecretModuleMock{}
+
+ actual := GetGiteaHook(s, m.DecryptSecret)
+
+ require.NotNil(t, actual)
+ require.IsType(t, &GiteaMeta{}, actual)
+ require.False(t, actual.AuthHeaderEnabled)
+ require.False(t, m.DecryptCalled, "Decrypt function unexpectedly called")
+ })
+
+ t.Run("Enabled auth headers", func(t *testing.T) {
+ s := &webhook_model.Webhook{
+ Type: webhook_model.GITEA,
+ Meta: `{"auth_header_enabled": true, "auth_header": "{\"name\": \"X-Test-Authorization\", \"type\": \"basic\", \"username\": \"test-user\", \"password\":\"test-password\"}"}`,
+ }
+
+ m := GiteaSecretModuleMock{}
+
+ actual := GetGiteaHook(s, m.DecryptSecret)
+
+ require.NotNil(t, actual)
+ require.IsType(t, &GiteaMeta{}, actual)
+ require.True(t, actual.AuthHeaderEnabled)
+ require.True(t, m.DecryptCalled, "Decrypt function was not called")
+
+ require.Equal(t, "X-Test-Authorization", actual.AuthHeader.Name)
+ require.Empty(t, actual.AuthHeaderData)
+ })
+
+ t.Run("Metadata parse error", func(t *testing.T) {
+ s := &webhook_model.Webhook{
+ Type: webhook_model.GITEA,
+ Meta: `{"`,
+ }
+
+ m := GiteaSecretModuleMock{}
+
+ actual := GetGiteaHook(s, m.DecryptSecret)
+
+ require.Nil(t, actual)
+ require.False(t, m.DecryptCalled, "Decrypt function unexpectedly called")
+ })
+
+ t.Run("AuthHeaderData parse error", func(t *testing.T) {
+ s := &webhook_model.Webhook{
+ Type: webhook_model.GITEA,
+ Meta: `{"auth_header_enabled": true, "auth_header": "{\""}`,
+ }
+
+ m := GiteaSecretModuleMock{}
+
+ actual := GetGiteaHook(s, m.DecryptSecret)
+
+ require.Nil(t, actual)
+ require.True(t, m.DecryptCalled, "Decrypt function was not called")
+ })
+
+ t.Run("Decryption error", func(t *testing.T) {
+ s := &webhook_model.Webhook{
+ Type: webhook_model.GITEA,
+ Meta: `{"auth_header_enabled": true, "auth_header": "{\"name\": \"X-Test-Authorization\", \"type\": \"basic\", \"username\": \"test-user\", \"password\":\"test-password\"}"}`,
+ }
+
+ m := GiteaSecretModuleMock{SimulateError: true}
+
+ actual := GetGiteaHook(s, m.DecryptSecret)
+
+ require.Nil(t, actual)
+ require.True(t, m.DecryptCalled, "Decrypt function was not called")
+ })
+}
+
+func TestCreateGiteaHook(t *testing.T) {
+ t.Run("Disabled auth headers", func(t *testing.T) {
+ m := GiteaSecretModuleMock{}
+
+ form := &forms.NewWebhookForm{
+ AuthHeaderActive: false,
+ }
+
+ actual, err := CreateGiteaHook(form, m.EncryptSecret)
+ expected := `{"auth_header_enabled":false}`
+
+ require.Nil(t, err)
+ require.Equal(t, expected, actual)
+ require.False(t, m.EncryptCalled, "Encrypt function unexpectedly called")
+ })
+
+ t.Run("Enabled auth headers (basic auth)", func(t *testing.T) {
+ m := GiteaSecretModuleMock{}
+
+ form := &forms.NewWebhookForm{
+ AuthHeaderActive: true,
+ AuthHeaderName: "Authorization",
+ AuthHeaderType: webhook_model.BASICAUTH,
+ AuthHeaderUsername: "test-user",
+ AuthHeaderPassword: "test-password",
+ }
+
+ actual, err := CreateGiteaHook(form, m.EncryptSecret)
+ expected := `{"auth_header_enabled":true,"auth_header":"{\"name\":\"Authorization\",\"type\":\"basic\",\"username\":\"test-user\",\"password\":\"test-password\"}"}`
+
+ require.Nil(t, err)
+ require.Equal(t, expected, actual)
+ require.True(t, m.EncryptCalled, "Encrypt function was not called")
+ })
+
+ t.Run("Enabled auth headers (token auth)", func(t *testing.T) {
+ m := GiteaSecretModuleMock{}
+
+ form := &forms.NewWebhookForm{
+ AuthHeaderActive: true,
+ AuthHeaderName: "Authorization",
+ AuthHeaderType: webhook_model.TOKENAUTH,
+ AuthHeaderToken: "test-token",
+ }
+
+ actual, err := CreateGiteaHook(form, m.EncryptSecret)
+ expected := `{"auth_header_enabled":true,"auth_header":"{\"name\":\"Authorization\",\"type\":\"token\",\"token\":\"test-token\"}"}`
+
+ require.Nil(t, err)
+ require.Equal(t, expected, actual)
+ require.True(t, m.EncryptCalled, "Encrypt function was not called")
+ })
+
+ t.Run("Encyption error", func(t *testing.T) {
+ m := GiteaSecretModuleMock{SimulateError: true}
+
+ form := &forms.NewWebhookForm{
+ AuthHeaderActive: true,
+ AuthHeaderName: "Authorization",
+ AuthHeaderType: webhook_model.TOKENAUTH,
+ AuthHeaderToken: "test-token",
+ }
+
+ actual, err := CreateGiteaHook(form, m.EncryptSecret)
+
+ require.NotNil(t, err)
+ require.Errorf(t, err, "Simulated error")
+ require.Empty(t, actual)
+ require.True(t, m.EncryptCalled, "Encrypt function was not called")
+ })
+}
From 4c13cdc93ad7938eba2dad95352797b0815b17df Mon Sep 17 00:00:00 2001
From: justusbunsi <61625851+justusbunsi@users.noreply.github.com>
Date: Wed, 6 Jul 2022 11:00:11 +0200
Subject: [PATCH 11/14] Apply code formatting
Signed-off-by: justusbunsi <61625851+justusbunsi@users.noreply.github.com>
---
services/webhook/gitea_test.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/services/webhook/gitea_test.go b/services/webhook/gitea_test.go
index 1f0445db9189b..9d5653a9d8d3d 100644
--- a/services/webhook/gitea_test.go
+++ b/services/webhook/gitea_test.go
@@ -20,7 +20,7 @@ type GiteaSecretModuleMock struct {
SimulateError bool
}
-func (m *GiteaSecretModuleMock) DecryptSecret(key string, cipherhex string) (string, error) {
+func (m *GiteaSecretModuleMock) DecryptSecret(key, cipherhex string) (string, error) {
m.DecryptCalled = true
if m.SimulateError {
From b69dd1595045644e353095e18ee8112c976f1975 Mon Sep 17 00:00:00 2001
From: justusbunsi <61625851+justusbunsi@users.noreply.github.com>
Date: Wed, 6 Jul 2022 11:25:09 +0200
Subject: [PATCH 12/14] Reword header content types
Signed-off-by: justusbunsi <61625851+justusbunsi@users.noreply.github.com>
---
options/locale/locale_en-US.ini | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index 9afdbc45e2131..2c72ed6666b51 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -1926,8 +1926,8 @@ settings.webhook.auth_header.section = Authorization Header
settings.webhook.auth_header.description = Add authorization header to webhook delivery.
settings.webhook.auth_header.name = Header name
settings.webhook.auth_header.type = Header content type
-settings.webhook.auth_header.type_basic = Basic authorization
-settings.webhook.auth_header.type_token = Token authorization
+settings.webhook.auth_header.type_basic = Basic authentication
+settings.webhook.auth_header.type_token = Token authentication
settings.webhook.auth_header.username = Username
settings.webhook.auth_header.password = Password
settings.webhook.auth_header.token = Token
From a4ad516496edc26257c845af8071ebfed93536fb Mon Sep 17 00:00:00 2001
From: justusbunsi <61625851+justusbunsi@users.noreply.github.com>
Date: Wed, 6 Jul 2022 11:31:27 +0200
Subject: [PATCH 13/14] Add section to docs
Signed-off-by: justusbunsi <61625851+justusbunsi@users.noreply.github.com>
---
docs/content/doc/features/webhooks.en-us.md | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/docs/content/doc/features/webhooks.en-us.md b/docs/content/doc/features/webhooks.en-us.md
index 2dba7b7f83c74..fdceb3d326570 100644
--- a/docs/content/doc/features/webhooks.en-us.md
+++ b/docs/content/doc/features/webhooks.en-us.md
@@ -188,3 +188,7 @@ if (json_last_error() !== JSON_ERROR_NONE) {
```
There is a Test Delivery button in the webhook settings that allows to test the configuration as well as a list of the most Recent Deliveries.
+
+### Authorization header (Gitea hook only)
+
+**With 1.18**, Gitea hooks can be configured to send an [authorization header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization) to the webhook target. Supported header types are _Basic Authentication_ and _Token Authentication_. The header key can be changed in case the webhook target requires a different one. The key defaults to `Authorization`.
From 96c608cb576fa795a4f3d8e9102136f0a7be57cc Mon Sep 17 00:00:00 2001
From: justusbunsi <61625851+justusbunsi@users.noreply.github.com>
Date: Wed, 6 Jul 2022 12:21:05 +0200
Subject: [PATCH 14/14] Apply frontend formatting
Signed-off-by: justusbunsi <61625851+justusbunsi@users.noreply.github.com>
---
web_src/js/features/comp/WebHookEditor.js | 24 +++++++++++------------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/web_src/js/features/comp/WebHookEditor.js b/web_src/js/features/comp/WebHookEditor.js
index 7cce08a1445d4..760c119b5c2ec 100644
--- a/web_src/js/features/comp/WebHookEditor.js
+++ b/web_src/js/features/comp/WebHookEditor.js
@@ -16,20 +16,20 @@ const initAuthenticationHeaderSection = function() {
const $tokenAuthFields = $authHeaderSection.find('.token-auth');
if (isBasicAuth) {
- $basicAuthFields.addClass("required");
- $basicAuthFields.find('input').attr("required", "");
+ $basicAuthFields.addClass('required');
+ $basicAuthFields.find('input').attr('required', '');
$basicAuthFields.show();
- $tokenAuthFields.removeClass("required");
- $tokenAuthFields.find('input').removeAttr("required");
+ $tokenAuthFields.removeClass('required');
+ $tokenAuthFields.find('input').removeAttr('required');
$tokenAuthFields.hide();
} else {
- $basicAuthFields.removeClass("required");
- $basicAuthFields.find('input').removeAttr("required");
+ $basicAuthFields.removeClass('required');
+ $basicAuthFields.find('input').removeAttr('required');
$basicAuthFields.hide();
- $tokenAuthFields.addClass("required");
- $tokenAuthFields.find('input').attr("required", "");
+ $tokenAuthFields.addClass('required');
+ $tokenAuthFields.find('input').attr('required', '');
$tokenAuthFields.show();
}
};
@@ -37,15 +37,15 @@ const initAuthenticationHeaderSection = function() {
const updateHeaderCheckbox = function() {
if ($checkbox.is(':checked')) {
const $headerName = $authHeaderSection.find('#auth_header_name');
- $headerName.attr("required", "");
- $headerName.parent().addClass("required");
+ $headerName.attr('required', '');
+ $headerName.parent().addClass('required');
$headerName.parent().show();
$authHeaderSection.find('#auth_header_type').parent().parent().show();
updateHeaderContentType();
} else {
$authHeaderSection.find('.auth-header').hide();
- $authHeaderSection.find('.auth-header').removeClass("required");
- $authHeaderSection.find('.auth-header input').removeAttr("required");
+ $authHeaderSection.find('.auth-header').removeClass('required');
+ $authHeaderSection.find('.auth-header input').removeAttr('required');
}
};