summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoroliverpool <3864879+oliverpool@users.noreply.github.com>2022-08-11 17:48:23 +0200
committerGitHub <noreply@github.com>2022-08-11 17:48:23 +0200
commitc81b26b0e52f55fcdf94b50a14bca3dfc375e2a9 (patch)
treeacccd4400733b5505a86ca668ceb4167119789fe
parent2b4d43dd4d7388ff73e99c143008ad4663142e61 (diff)
downloadgitea-c81b26b0e52f55fcdf94b50a14bca3dfc375e2a9.tar.gz
gitea-c81b26b0e52f55fcdf94b50a14bca3dfc375e2a9.zip
refactor webhook *NewPost (#20729)
* refactor webhook *NewPost * remove empty values * always show errs.Message * remove utils.IsValidSlackChannel * move IsValidSlackChannel to services/webhook package * binding: handle empty Message case * make IsValidSlackChannel more strict
-rw-r--r--modules/web/middleware/binding.go11
-rw-r--r--routers/api/v1/utils/hook.go6
-rw-r--r--routers/utils/utils.go19
-rw-r--r--routers/utils/utils_test.go17
-rw-r--r--routers/web/repo/webhook.go577
-rw-r--r--services/forms/repo_form.go14
-rw-r--r--services/webhook/slack.go11
-rw-r--r--services/webhook/slack_test.go19
8 files changed, 179 insertions, 495 deletions
diff --git a/modules/web/middleware/binding.go b/modules/web/middleware/binding.go
index 88a3920f6e..636e655b9e 100644
--- a/modules/web/middleware/binding.go
+++ b/modules/web/middleware/binding.go
@@ -136,7 +136,16 @@ func Validate(errs binding.Errors, data map[string]interface{}, f Form, l transl
case validation.ErrRegexPattern:
data["ErrorMsg"] = trName + l.Tr("form.regex_pattern_error", errs[0].Message)
default:
- data["ErrorMsg"] = l.Tr("form.unknown_error") + " " + errs[0].Classification
+ msg := errs[0].Classification
+ if msg != "" && errs[0].Message != "" {
+ msg += ": "
+ }
+
+ msg += errs[0].Message
+ if msg == "" {
+ msg = l.Tr("form.unknown_error")
+ }
+ data["ErrorMsg"] = trName + ": " + msg
}
return errs
}
diff --git a/routers/api/v1/utils/hook.go b/routers/api/v1/utils/hook.go
index f0dc595ad5..ba008f587c 100644
--- a/routers/api/v1/utils/hook.go
+++ b/routers/api/v1/utils/hook.go
@@ -15,7 +15,6 @@ import (
"code.gitea.io/gitea/modules/json"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/routers/utils"
webhook_service "code.gitea.io/gitea/services/webhook"
)
@@ -141,14 +140,15 @@ func addHook(ctx *context.APIContext, form *api.CreateHookOption, orgID, repoID
ctx.Error(http.StatusUnprocessableEntity, "", "Missing config option: channel")
return nil, false
}
+ channel = strings.TrimSpace(channel)
- if !utils.IsValidSlackChannel(channel) {
+ if !webhook_service.IsValidSlackChannel(channel) {
ctx.Error(http.StatusBadRequest, "", "Invalid slack channel name")
return nil, false
}
meta, err := json.Marshal(&webhook_service.SlackMeta{
- Channel: strings.TrimSpace(channel),
+ Channel: channel,
Username: form.Config["username"],
IconURL: form.Config["icon_url"],
Color: form.Config["color"],
diff --git a/routers/utils/utils.go b/routers/utils/utils.go
index f15bc1e62e..66eaa1d9ce 100644
--- a/routers/utils/utils.go
+++ b/routers/utils/utils.go
@@ -20,25 +20,6 @@ func RemoveUsernameParameterSuffix(name string) string {
return name
}
-// IsValidSlackChannel validates a channel name conforms to what slack expects.
-// It makes sure a channel name cannot be empty and invalid ( only an # )
-func IsValidSlackChannel(channelName string) bool {
- switch len(strings.TrimSpace(channelName)) {
- case 0:
- return false
- case 1:
- // Keep default behaviour where a channel name is still
- // valid without an #
- // But if it contains only an #, it should be regarded as
- // invalid
- if channelName[0] == '#' {
- return false
- }
- }
-
- return true
-}
-
// SanitizeFlashErrorString will sanitize a flash error string
func SanitizeFlashErrorString(x string) string {
return strings.ReplaceAll(html.EscapeString(x), "\n", "<br>")
diff --git a/routers/utils/utils_test.go b/routers/utils/utils_test.go
index f49ed77b6f..42cf948e30 100644
--- a/routers/utils/utils_test.go
+++ b/routers/utils/utils_test.go
@@ -18,23 +18,6 @@ func TestRemoveUsernameParameterSuffix(t *testing.T) {
assert.Equal(t, "", RemoveUsernameParameterSuffix(""))
}
-func TestIsValidSlackChannel(t *testing.T) {
- tt := []struct {
- channelName string
- expected bool
- }{
- {"gitea", true},
- {" ", false},
- {"#", false},
- {"gitea ", true},
- {" gitea", true},
- }
-
- for _, v := range tt {
- assert.Equal(t, v.expected, IsValidSlackChannel(v.channelName))
- }
-}
-
func TestIsExternalURL(t *testing.T) {
setting.AppURL = "https://try.gitea.io/"
type test struct {
diff --git a/routers/web/repo/webhook.go b/routers/web/repo/webhook.go
index a9b14ee21f..d4419a1e10 100644
--- a/routers/web/repo/webhook.go
+++ b/routers/web/repo/webhook.go
@@ -185,14 +185,22 @@ func ParseHookEvent(form forms.WebhookForm) *webhook.HookEvent {
}
}
-// GiteaHooksNewPost response for creating Gitea webhook
-func GiteaHooksNewPost(ctx *context.Context) {
- form := web.GetForm(ctx).(*forms.NewWebhookForm)
+type webhookCreationParams struct {
+ URL string
+ ContentType webhook.HookContentType
+ Secret string
+ HTTPMethod string
+ WebhookForm forms.WebhookForm
+ Type string
+ Meta interface{}
+}
+
+func createWebhook(ctx *context.Context, params webhookCreationParams) {
ctx.Data["Title"] = ctx.Tr("repo.settings.add_webhook")
ctx.Data["PageIsSettingsHooks"] = true
ctx.Data["PageIsSettingsHooksNew"] = true
ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook.HookEvent{}}
- ctx.Data["HookType"] = webhook.GITEA
+ ctx.Data["HookType"] = params.Type
orCtx, err := getOrgRepoCtx(ctx)
if err != nil {
@@ -206,20 +214,25 @@ func GiteaHooksNewPost(ctx *context.Context) {
return
}
- contentType := webhook.ContentTypeJSON
- if webhook.HookContentType(form.ContentType) == webhook.ContentTypeForm {
- contentType = webhook.ContentTypeForm
+ var meta []byte
+ if params.Meta != nil {
+ meta, err = json.Marshal(params.Meta)
+ if err != nil {
+ ctx.ServerError("Marshal", err)
+ return
+ }
}
w := &webhook.Webhook{
RepoID: orCtx.RepoID,
- URL: form.PayloadURL,
- HTTPMethod: form.HTTPMethod,
- ContentType: contentType,
- Secret: form.Secret,
- HookEvent: ParseHookEvent(form.WebhookForm),
- IsActive: form.Active,
- Type: webhook.GITEA,
+ URL: params.URL,
+ HTTPMethod: params.HTTPMethod,
+ ContentType: params.ContentType,
+ Secret: params.Secret,
+ HookEvent: ParseHookEvent(params.WebhookForm),
+ IsActive: params.WebhookForm.Active,
+ Type: params.Type,
+ Meta: string(meta),
OrgID: orCtx.OrgID,
IsSystemWebhook: orCtx.IsSystemWebhook,
}
@@ -235,503 +248,175 @@ func GiteaHooksNewPost(ctx *context.Context) {
ctx.Redirect(orCtx.Link)
}
-// GogsHooksNewPost response for creating webhook
-func GogsHooksNewPost(ctx *context.Context) {
- form := web.GetForm(ctx).(*forms.NewGogshookForm)
- newGogsWebhookPost(ctx, *form, webhook.GOGS)
-}
-
-// newGogsWebhookPost response for creating gogs hook
-func newGogsWebhookPost(ctx *context.Context, form forms.NewGogshookForm, kind webhook.HookType) {
- ctx.Data["Title"] = ctx.Tr("repo.settings.add_webhook")
- ctx.Data["PageIsSettingsHooks"] = true
- ctx.Data["PageIsSettingsHooksNew"] = true
- ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook.HookEvent{}}
- ctx.Data["HookType"] = webhook.GOGS
+// GiteaHooksNewPost response for creating Gitea webhook
+func GiteaHooksNewPost(ctx *context.Context) {
+ form := web.GetForm(ctx).(*forms.NewWebhookForm)
- orCtx, err := getOrgRepoCtx(ctx)
- if err != nil {
- ctx.ServerError("getOrgRepoCtx", err)
- return
+ contentType := webhook.ContentTypeJSON
+ if webhook.HookContentType(form.ContentType) == webhook.ContentTypeForm {
+ contentType = webhook.ContentTypeForm
}
- ctx.Data["BaseLink"] = orCtx.LinkNew
- if ctx.HasError() {
- ctx.HTML(http.StatusOK, orCtx.NewTemplate)
- return
- }
+ createWebhook(ctx, webhookCreationParams{
+ URL: form.PayloadURL,
+ ContentType: contentType,
+ Secret: form.Secret,
+ HTTPMethod: form.HTTPMethod,
+ WebhookForm: form.WebhookForm,
+ Type: webhook.GITEA,
+ })
+}
+
+// GogsHooksNewPost response for creating webhook
+func GogsHooksNewPost(ctx *context.Context) {
+ form := web.GetForm(ctx).(*forms.NewGogshookForm)
contentType := webhook.ContentTypeJSON
if webhook.HookContentType(form.ContentType) == webhook.ContentTypeForm {
contentType = webhook.ContentTypeForm
}
- w := &webhook.Webhook{
- RepoID: orCtx.RepoID,
- URL: form.PayloadURL,
- ContentType: contentType,
- Secret: form.Secret,
- HookEvent: ParseHookEvent(form.WebhookForm),
- IsActive: form.Active,
- Type: kind,
- OrgID: orCtx.OrgID,
- IsSystemWebhook: orCtx.IsSystemWebhook,
- }
- if err := w.UpdateEvent(); err != nil {
- ctx.ServerError("UpdateEvent", err)
- return
- } else if err := webhook.CreateWebhook(ctx, w); err != nil {
- ctx.ServerError("CreateWebhook", err)
- return
- }
-
- ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
- ctx.Redirect(orCtx.Link)
+ createWebhook(ctx, webhookCreationParams{
+ URL: form.PayloadURL,
+ ContentType: contentType,
+ Secret: form.Secret,
+ WebhookForm: form.WebhookForm,
+ Type: webhook.GOGS,
+ })
}
// DiscordHooksNewPost response for creating discord hook
func DiscordHooksNewPost(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.NewDiscordHookForm)
- ctx.Data["Title"] = ctx.Tr("repo.settings")
- ctx.Data["PageIsSettingsHooks"] = true
- ctx.Data["PageIsSettingsHooksNew"] = true
- ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook.HookEvent{}}
- ctx.Data["HookType"] = webhook.DISCORD
- orCtx, err := getOrgRepoCtx(ctx)
- if err != nil {
- ctx.ServerError("getOrgRepoCtx", err)
- return
- }
-
- if ctx.HasError() {
- ctx.HTML(http.StatusOK, orCtx.NewTemplate)
- return
- }
-
- meta, err := json.Marshal(&webhook_service.DiscordMeta{
- Username: form.Username,
- IconURL: form.IconURL,
+ createWebhook(ctx, webhookCreationParams{
+ URL: form.PayloadURL,
+ ContentType: webhook.ContentTypeJSON,
+ WebhookForm: form.WebhookForm,
+ Type: webhook.DISCORD,
+ Meta: &webhook_service.DiscordMeta{
+ Username: form.Username,
+ IconURL: form.IconURL,
+ },
})
- if err != nil {
- ctx.ServerError("Marshal", err)
- return
- }
-
- w := &webhook.Webhook{
- RepoID: orCtx.RepoID,
- URL: form.PayloadURL,
- ContentType: webhook.ContentTypeJSON,
- HookEvent: ParseHookEvent(form.WebhookForm),
- IsActive: form.Active,
- Type: webhook.DISCORD,
- Meta: string(meta),
- OrgID: orCtx.OrgID,
- IsSystemWebhook: orCtx.IsSystemWebhook,
- }
- if err := w.UpdateEvent(); err != nil {
- ctx.ServerError("UpdateEvent", err)
- return
- } else if err := webhook.CreateWebhook(ctx, w); err != nil {
- ctx.ServerError("CreateWebhook", err)
- return
- }
-
- ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
- ctx.Redirect(orCtx.Link)
}
// DingtalkHooksNewPost response for creating dingtalk hook
func DingtalkHooksNewPost(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.NewDingtalkHookForm)
- ctx.Data["Title"] = ctx.Tr("repo.settings")
- ctx.Data["PageIsSettingsHooks"] = true
- ctx.Data["PageIsSettingsHooksNew"] = true
- ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook.HookEvent{}}
- ctx.Data["HookType"] = webhook.DINGTALK
-
- orCtx, err := getOrgRepoCtx(ctx)
- if err != nil {
- ctx.ServerError("getOrgRepoCtx", err)
- return
- }
-
- if ctx.HasError() {
- ctx.HTML(http.StatusOK, orCtx.NewTemplate)
- return
- }
- w := &webhook.Webhook{
- RepoID: orCtx.RepoID,
- URL: form.PayloadURL,
- ContentType: webhook.ContentTypeJSON,
- HookEvent: ParseHookEvent(form.WebhookForm),
- IsActive: form.Active,
- Type: webhook.DINGTALK,
- Meta: "",
- OrgID: orCtx.OrgID,
- IsSystemWebhook: orCtx.IsSystemWebhook,
- }
- if err := w.UpdateEvent(); err != nil {
- ctx.ServerError("UpdateEvent", err)
- return
- } else if err := webhook.CreateWebhook(ctx, w); err != nil {
- ctx.ServerError("CreateWebhook", err)
- return
- }
-
- ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
- ctx.Redirect(orCtx.Link)
+ createWebhook(ctx, webhookCreationParams{
+ URL: form.PayloadURL,
+ ContentType: webhook.ContentTypeJSON,
+ WebhookForm: form.WebhookForm,
+ Type: webhook.DINGTALK,
+ })
}
// TelegramHooksNewPost response for creating telegram hook
func TelegramHooksNewPost(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.NewTelegramHookForm)
- ctx.Data["Title"] = ctx.Tr("repo.settings")
- ctx.Data["PageIsSettingsHooks"] = true
- ctx.Data["PageIsSettingsHooksNew"] = true
- ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook.HookEvent{}}
- ctx.Data["HookType"] = webhook.TELEGRAM
-
- orCtx, err := getOrgRepoCtx(ctx)
- if err != nil {
- ctx.ServerError("getOrgRepoCtx", err)
- return
- }
-
- if ctx.HasError() {
- ctx.HTML(http.StatusOK, orCtx.NewTemplate)
- return
- }
- meta, err := json.Marshal(&webhook_service.TelegramMeta{
- BotToken: form.BotToken,
- ChatID: form.ChatID,
+ createWebhook(ctx, webhookCreationParams{
+ URL: fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage?chat_id=%s", url.PathEscape(form.BotToken), url.QueryEscape(form.ChatID)),
+ ContentType: webhook.ContentTypeJSON,
+ WebhookForm: form.WebhookForm,
+ Type: webhook.TELEGRAM,
+ Meta: &webhook_service.TelegramMeta{
+ BotToken: form.BotToken,
+ ChatID: form.ChatID,
+ },
})
- if err != nil {
- ctx.ServerError("Marshal", err)
- return
- }
-
- w := &webhook.Webhook{
- RepoID: orCtx.RepoID,
- URL: fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage?chat_id=%s", url.PathEscape(form.BotToken), url.QueryEscape(form.ChatID)),
- ContentType: webhook.ContentTypeJSON,
- HookEvent: ParseHookEvent(form.WebhookForm),
- IsActive: form.Active,
- Type: webhook.TELEGRAM,
- Meta: string(meta),
- OrgID: orCtx.OrgID,
- IsSystemWebhook: orCtx.IsSystemWebhook,
- }
- if err := w.UpdateEvent(); err != nil {
- ctx.ServerError("UpdateEvent", err)
- return
- } else if err := webhook.CreateWebhook(ctx, w); err != nil {
- ctx.ServerError("CreateWebhook", err)
- return
- }
-
- ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
- ctx.Redirect(orCtx.Link)
}
// MatrixHooksNewPost response for creating a Matrix hook
func MatrixHooksNewPost(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.NewMatrixHookForm)
- ctx.Data["Title"] = ctx.Tr("repo.settings")
- ctx.Data["PageIsSettingsHooks"] = true
- ctx.Data["PageIsSettingsHooksNew"] = true
- ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook.HookEvent{}}
- ctx.Data["HookType"] = webhook.MATRIX
- orCtx, err := getOrgRepoCtx(ctx)
- if err != nil {
- ctx.ServerError("getOrgRepoCtx", err)
- return
- }
-
- if ctx.HasError() {
- ctx.HTML(http.StatusOK, orCtx.NewTemplate)
- return
- }
-
- meta, err := json.Marshal(&webhook_service.MatrixMeta{
- HomeserverURL: form.HomeserverURL,
- Room: form.RoomID,
- AccessToken: form.AccessToken,
- MessageType: form.MessageType,
+ createWebhook(ctx, webhookCreationParams{
+ URL: fmt.Sprintf("%s/_matrix/client/r0/rooms/%s/send/m.room.message", form.HomeserverURL, url.PathEscape(form.RoomID)),
+ ContentType: webhook.ContentTypeJSON,
+ HTTPMethod: http.MethodPut,
+ WebhookForm: form.WebhookForm,
+ Type: webhook.MATRIX,
+ Meta: &webhook_service.MatrixMeta{
+ HomeserverURL: form.HomeserverURL,
+ Room: form.RoomID,
+ AccessToken: form.AccessToken,
+ MessageType: form.MessageType,
+ },
})
- if err != nil {
- ctx.ServerError("Marshal", err)
- return
- }
-
- w := &webhook.Webhook{
- RepoID: orCtx.RepoID,
- URL: fmt.Sprintf("%s/_matrix/client/r0/rooms/%s/send/m.room.message", form.HomeserverURL, url.PathEscape(form.RoomID)),
- ContentType: webhook.ContentTypeJSON,
- HTTPMethod: "PUT",
- HookEvent: ParseHookEvent(form.WebhookForm),
- IsActive: form.Active,
- Type: webhook.MATRIX,
- Meta: string(meta),
- OrgID: orCtx.OrgID,
- IsSystemWebhook: orCtx.IsSystemWebhook,
- }
- if err := w.UpdateEvent(); err != nil {
- ctx.ServerError("UpdateEvent", err)
- return
- } else if err := webhook.CreateWebhook(ctx, w); err != nil {
- ctx.ServerError("CreateWebhook", err)
- return
- }
-
- ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
- ctx.Redirect(orCtx.Link)
}
// MSTeamsHooksNewPost response for creating MS Teams hook
func MSTeamsHooksNewPost(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.NewMSTeamsHookForm)
- ctx.Data["Title"] = ctx.Tr("repo.settings")
- ctx.Data["PageIsSettingsHooks"] = true
- ctx.Data["PageIsSettingsHooksNew"] = true
- ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook.HookEvent{}}
- ctx.Data["HookType"] = webhook.MSTEAMS
-
- orCtx, err := getOrgRepoCtx(ctx)
- if err != nil {
- ctx.ServerError("getOrgRepoCtx", err)
- return
- }
- if ctx.HasError() {
- ctx.HTML(http.StatusOK, orCtx.NewTemplate)
- return
- }
-
- w := &webhook.Webhook{
- RepoID: orCtx.RepoID,
- URL: form.PayloadURL,
- ContentType: webhook.ContentTypeJSON,
- HookEvent: ParseHookEvent(form.WebhookForm),
- IsActive: form.Active,
- Type: webhook.MSTEAMS,
- Meta: "",
- OrgID: orCtx.OrgID,
- IsSystemWebhook: orCtx.IsSystemWebhook,
- }
- if err := w.UpdateEvent(); err != nil {
- ctx.ServerError("UpdateEvent", err)
- return
- } else if err := webhook.CreateWebhook(ctx, w); err != nil {
- ctx.ServerError("CreateWebhook", err)
- return
- }
-
- ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
- ctx.Redirect(orCtx.Link)
+ createWebhook(ctx, webhookCreationParams{
+ URL: form.PayloadURL,
+ ContentType: webhook.ContentTypeJSON,
+ WebhookForm: form.WebhookForm,
+ Type: webhook.MSTEAMS,
+ })
}
// SlackHooksNewPost response for creating slack hook
func SlackHooksNewPost(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.NewSlackHookForm)
- ctx.Data["Title"] = ctx.Tr("repo.settings")
- ctx.Data["PageIsSettingsHooks"] = true
- ctx.Data["PageIsSettingsHooksNew"] = true
- ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook.HookEvent{}}
- ctx.Data["HookType"] = webhook.SLACK
-
- orCtx, err := getOrgRepoCtx(ctx)
- if err != nil {
- ctx.ServerError("getOrgRepoCtx", err)
- return
- }
-
- if ctx.HasError() {
- ctx.HTML(http.StatusOK, orCtx.NewTemplate)
- return
- }
-
- if form.HasInvalidChannel() {
- ctx.Flash.Error(ctx.Tr("repo.settings.add_webhook.invalid_channel_name"))
- ctx.Redirect(orCtx.LinkNew + "/slack/new")
- return
- }
- meta, err := json.Marshal(&webhook_service.SlackMeta{
- Channel: strings.TrimSpace(form.Channel),
- Username: form.Username,
- IconURL: form.IconURL,
- Color: form.Color,
+ createWebhook(ctx, webhookCreationParams{
+ URL: form.PayloadURL,
+ ContentType: webhook.ContentTypeJSON,
+ WebhookForm: form.WebhookForm,
+ Type: webhook.SLACK,
+ Meta: &webhook_service.SlackMeta{
+ Channel: strings.TrimSpace(form.Channel),
+ Username: form.Username,
+ IconURL: form.IconURL,
+ Color: form.Color,
+ },
})
- if err != nil {
- ctx.ServerError("Marshal", err)
- return
- }
-
- w := &webhook.Webhook{
- RepoID: orCtx.RepoID,
- URL: form.PayloadURL,
- ContentType: webhook.ContentTypeJSON,
- HookEvent: ParseHookEvent(form.WebhookForm),
- IsActive: form.Active,
- Type: webhook.SLACK,
- Meta: string(meta),
- OrgID: orCtx.OrgID,
- IsSystemWebhook: orCtx.IsSystemWebhook,
- }
- if err := w.UpdateEvent(); err != nil {
- ctx.ServerError("UpdateEvent", err)
- return
- } else if err := webhook.CreateWebhook(ctx, w); err != nil {
- ctx.ServerError("CreateWebhook", err)
- return
- }
-
- ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
- ctx.Redirect(orCtx.Link)
}
// FeishuHooksNewPost response for creating feishu hook
func FeishuHooksNewPost(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.NewFeishuHookForm)
- ctx.Data["Title"] = ctx.Tr("repo.settings")
- ctx.Data["PageIsSettingsHooks"] = true
- ctx.Data["PageIsSettingsHooksNew"] = true
- ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook.HookEvent{}}
- ctx.Data["HookType"] = webhook.FEISHU
-
- orCtx, err := getOrgRepoCtx(ctx)
- if err != nil {
- ctx.ServerError("getOrgRepoCtx", err)
- return
- }
- if ctx.HasError() {
- ctx.HTML(http.StatusOK, orCtx.NewTemplate)
- return
- }
-
- w := &webhook.Webhook{
- RepoID: orCtx.RepoID,
- URL: form.PayloadURL,
- ContentType: webhook.ContentTypeJSON,
- HookEvent: ParseHookEvent(form.WebhookForm),
- IsActive: form.Active,
- Type: webhook.FEISHU,
- Meta: "",
- OrgID: orCtx.OrgID,
- IsSystemWebhook: orCtx.IsSystemWebhook,
- }
- if err := w.UpdateEvent(); err != nil {
- ctx.ServerError("UpdateEvent", err)
- return
- } else if err := webhook.CreateWebhook(ctx, w); err != nil {
- ctx.ServerError("CreateWebhook", err)
- return
- }
-
- ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
- ctx.Redirect(orCtx.Link)
+ createWebhook(ctx, webhookCreationParams{
+ URL: form.PayloadURL,
+ ContentType: webhook.ContentTypeJSON,
+ WebhookForm: form.WebhookForm,
+ Type: webhook.FEISHU,
+ })
}
// WechatworkHooksNewPost response for creating wechatwork hook
func WechatworkHooksNewPost(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.NewWechatWorkHookForm)
- ctx.Data["Title"] = ctx.Tr("repo.settings")
- ctx.Data["PageIsSettingsHooks"] = true
- ctx.Data["PageIsSettingsHooksNew"] = true
- ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook.HookEvent{}}
- ctx.Data["HookType"] = webhook.WECHATWORK
-
- orCtx, err := getOrgRepoCtx(ctx)
- if err != nil {
- ctx.ServerError("getOrgRepoCtx", err)
- return
- }
-
- if ctx.HasError() {
- ctx.HTML(http.StatusOK, orCtx.NewTemplate)
- return
- }
-
- w := &webhook.Webhook{
- RepoID: orCtx.RepoID,
- URL: form.PayloadURL,
- ContentType: webhook.ContentTypeJSON,
- HookEvent: ParseHookEvent(form.WebhookForm),
- IsActive: form.Active,
- Type: webhook.WECHATWORK,
- Meta: "",
- OrgID: orCtx.OrgID,
- IsSystemWebhook: orCtx.IsSystemWebhook,
- }
- if err := w.UpdateEvent(); err != nil {
- ctx.ServerError("UpdateEvent", err)
- return
- } else if err := webhook.CreateWebhook(ctx, w); err != nil {
- ctx.ServerError("CreateWebhook", err)
- return
- }
-
- ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
- ctx.Redirect(orCtx.Link)
+ createWebhook(ctx, webhookCreationParams{
+ URL: form.PayloadURL,
+ ContentType: webhook.ContentTypeJSON,
+ WebhookForm: form.WebhookForm,
+ Type: webhook.WECHATWORK,
+ })
}
// PackagistHooksNewPost response for creating packagist hook
func PackagistHooksNewPost(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.NewPackagistHookForm)
- ctx.Data["Title"] = ctx.Tr("repo.settings")
- ctx.Data["PageIsSettingsHooks"] = true
- ctx.Data["PageIsSettingsHooksNew"] = true
- ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook.HookEvent{}}
- ctx.Data["HookType"] = webhook.PACKAGIST
- orCtx, err := getOrgRepoCtx(ctx)
- if err != nil {
- ctx.ServerError("getOrgRepoCtx", err)
- return
- }
-
- if ctx.HasError() {
- ctx.HTML(http.StatusOK, orCtx.NewTemplate)
- return
- }
-
- meta, err := json.Marshal(&webhook_service.PackagistMeta{
- Username: form.Username,
- APIToken: form.APIToken,
- PackageURL: form.PackageURL,
+ createWebhook(ctx, webhookCreationParams{
+ URL: fmt.Sprintf("https://packagist.org/api/update-package?username=%s&apiToken=%s", url.QueryEscape(form.Username), url.QueryEscape(form.APIToken)),
+ ContentType: webhook.ContentTypeJSON,
+ WebhookForm: form.WebhookForm,
+ Type: webhook.PACKAGIST,
+ Meta: &webhook_service.PackagistMeta{
+ Username: form.Username,
+ APIToken: form.APIToken,
+ PackageURL: form.PackageURL,
+ },
})
- if err != nil {
- ctx.ServerError("Marshal", err)
- return
- }
-
- w := &webhook.Webhook{
- RepoID: orCtx.RepoID,
- URL: fmt.Sprintf("https://packagist.org/api/update-package?username=%s&apiToken=%s", url.QueryEscape(form.Username), url.QueryEscape(form.APIToken)),
- ContentType: webhook.ContentTypeJSON,
- HookEvent: ParseHookEvent(form.WebhookForm),
- IsActive: form.Active,
- Type: webhook.PACKAGIST,
- Meta: string(meta),
- OrgID: orCtx.OrgID,
- IsSystemWebhook: orCtx.IsSystemWebhook,
- }
- if err := w.UpdateEvent(); err != nil {
- ctx.ServerError("UpdateEvent", err)
- return
- } else if err := webhook.CreateWebhook(ctx, w); err != nil {
- ctx.ServerError("CreateWebhook", err)
- return
- }
-
- ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
- ctx.Redirect(orCtx.Link)
}
func checkWebhook(ctx *context.Context) (*orgRepoCtx, *webhook.Webhook) {
@@ -894,12 +579,6 @@ func SlackHooksEditPost(ctx *context.Context) {
return
}
- if form.HasInvalidChannel() {
- ctx.Flash.Error(ctx.Tr("repo.settings.add_webhook.invalid_channel_name"))
- ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
- return
- }
-
meta, err := json.Marshal(&webhook_service.SlackMeta{
Channel: strings.TrimSpace(form.Channel),
Username: form.Username,
diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go
index afecc205f3..7a4a2123eb 100644
--- a/services/forms/repo_form.go
+++ b/services/forms/repo_form.go
@@ -17,7 +17,7 @@ import (
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web/middleware"
- "code.gitea.io/gitea/routers/utils"
+ "code.gitea.io/gitea/services/webhook"
"gitea.com/go-chi/binding"
)
@@ -305,14 +305,16 @@ type NewSlackHookForm struct {
// Validate validates the fields
func (f *NewSlackHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
+ if !webhook.IsValidSlackChannel(strings.TrimSpace(f.Channel)) {
+ errs = append(errs, binding.Error{
+ FieldNames: []string{"Channel"},
+ Classification: "",
+ Message: ctx.Tr("repo.settings.add_webhook.invalid_channel_name"),
+ })
+ }
return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
}
-// HasInvalidChannel validates the channel name is in the right format
-func (f NewSlackHookForm) HasInvalidChannel() bool {
- return !utils.IsValidSlackChannel(f.Channel)
-}
-
// NewDiscordHookForm form for creating discord hook
type NewDiscordHookForm struct {
PayloadURL string `binding:"Required;ValidUrl"`
diff --git a/services/webhook/slack.go b/services/webhook/slack.go
index 11e1d3c081..e3d0d406de 100644
--- a/services/webhook/slack.go
+++ b/services/webhook/slack.go
@@ -7,6 +7,7 @@ package webhook
import (
"errors"
"fmt"
+ "regexp"
"strings"
webhook_model "code.gitea.io/gitea/models/webhook"
@@ -286,3 +287,13 @@ func GetSlackPayload(p api.Payloader, event webhook_model.HookEventType, meta st
return convertPayloader(s, p, event)
}
+
+var slackChannel = regexp.MustCompile(`^#?[a-z0-9_-]{1,80}$`)
+
+// IsValidSlackChannel validates a channel name conforms to what slack expects:
+// https://api.slack.com/methods/conversations.rename#naming
+// Conversation names can only contain lowercase letters, numbers, hyphens, and underscores, and must be 80 characters or less.
+// Gitea accepts if it starts with a #.
+func IsValidSlackChannel(name string) bool {
+ return slackChannel.MatchString(name)
+}
diff --git a/services/webhook/slack_test.go b/services/webhook/slack_test.go
index 8278afb69a..0f08785d25 100644
--- a/services/webhook/slack_test.go
+++ b/services/webhook/slack_test.go
@@ -170,3 +170,22 @@ func TestSlackJSONPayload(t *testing.T) {
require.NoError(t, err)
assert.NotEmpty(t, json)
}
+
+func TestIsValidSlackChannel(t *testing.T) {
+ tt := []struct {
+ channelName string
+ expected bool
+ }{
+ {"gitea", true},
+ {"#gitea", true},
+ {" ", false},
+ {"#", false},
+ {" #", false},
+ {"gitea ", false},
+ {" gitea", false},
+ }
+
+ for _, v := range tt {
+ assert.Equal(t, v.expected, IsValidSlackChannel(v.channelName))
+ }
+}