aboutsummaryrefslogtreecommitdiff
path: root/internal/route/repo
diff options
context:
space:
mode:
authorᴜɴᴋɴᴡᴏɴ <u@gogs.io>2020-03-22 22:07:22 +0800
committerGitHub <noreply@github.com>2020-03-22 22:07:22 +0800
commit22717a1c064511cf37c46af5e650baf7184cf25b (patch)
treef98bb991145605567f8b43506a7add855db0a90f /internal/route/repo
parent82e511ddb1d1e98ebe6b1931766b0835fc066883 (diff)
webhook: overhaul route handlers (#6002)
* Overual route handlers and fixes #5366 * Merge routes for repo and org * Inject OrgRepoContext * DRY validateWebhook * DRY c.HasError * Add tests * Update CHANGELOG
Diffstat (limited to 'internal/route/repo')
-rw-r--r--internal/route/repo/webhook.go528
-rw-r--r--internal/route/repo/webhook_test.go72
2 files changed, 318 insertions, 282 deletions
diff --git a/internal/route/repo/webhook.go b/internal/route/repo/webhook.go
index 32c2069b..8e4b3a11 100644
--- a/internal/route/repo/webhook.go
+++ b/internal/route/repo/webhook.go
@@ -7,13 +7,13 @@ package repo
import (
"fmt"
"net/http"
+ "net/url"
"strings"
- jsoniter "github.com/json-iterator/go"
- "github.com/unknwon/com"
-
- git "github.com/gogs/git-module"
+ "github.com/gogs/git-module"
api "github.com/gogs/go-gogs-client"
+ jsoniter "github.com/json-iterator/go"
+ "gopkg.in/macaron.v1"
"gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/context"
@@ -23,89 +23,163 @@ import (
)
const (
- WEBHOOKS = "repo/settings/webhook/base"
- WEBHOOK_NEW = "repo/settings/webhook/new"
- ORG_WEBHOOK_NEW = "org/settings/webhook_new"
+ tmplRepoSettingsWebhooks = "repo/settings/webhook/base"
+ tmplRepoSettingsWebhookNew = "repo/settings/webhook/new"
+ tmplOrgSettingsWebhooks = "org/settings/webhooks"
+ tmplOrgSettingsWebhookNew = "org/settings/webhook_new"
)
-func Webhooks(c *context.Context) {
- c.Data["Title"] = c.Tr("repo.settings.hooks")
- c.Data["PageIsSettingsHooks"] = true
- c.Data["BaseLink"] = c.Repo.RepoLink
- c.Data["Description"] = c.Tr("repo.settings.hooks_desc", "https://github.com/gogs/docs-api/blob/master/Repositories/Webhooks.md")
- c.Data["Types"] = conf.Webhook.Types
-
- ws, err := db.GetWebhooksByRepoID(c.Repo.Repository.ID)
- if err != nil {
- c.Error(err, "get webhooks by repository ID")
- return
+func InjectOrgRepoContext() macaron.Handler {
+ return func(c *context.Context) {
+ orCtx, err := getOrgRepoContext(c)
+ if err != nil {
+ c.Error(err, "get organization or repository context")
+ return
+ }
+ c.Map(orCtx)
}
- c.Data["Webhooks"] = ws
-
- c.Success(WEBHOOKS)
}
-type OrgRepoCtx struct {
- OrgID int64
- RepoID int64
- Link string
- NewTemplate string
+type orgRepoContext struct {
+ OrgID int64
+ RepoID int64
+ Link string
+ TmplList string
+ TmplNew string
}
-// getOrgRepoCtx determines whether this is a repo context or organization context.
-func getOrgRepoCtx(c *context.Context) (*OrgRepoCtx, error) {
+// getOrgRepoContext determines whether this is a repo context or organization context.
+func getOrgRepoContext(c *context.Context) (*orgRepoContext, error) {
if len(c.Repo.RepoLink) > 0 {
- c.Data["PageIsRepositoryContext"] = true
- return &OrgRepoCtx{
- RepoID: c.Repo.Repository.ID,
- Link: c.Repo.RepoLink,
- NewTemplate: WEBHOOK_NEW,
+ c.PageIs("RepositoryContext")
+ return &orgRepoContext{
+ RepoID: c.Repo.Repository.ID,
+ Link: c.Repo.RepoLink,
+ TmplList: tmplRepoSettingsWebhooks,
+ TmplNew: tmplRepoSettingsWebhookNew,
}, nil
}
if len(c.Org.OrgLink) > 0 {
- c.Data["PageIsOrganizationContext"] = true
- return &OrgRepoCtx{
- OrgID: c.Org.Organization.ID,
- Link: c.Org.OrgLink,
- NewTemplate: ORG_WEBHOOK_NEW,
+ c.PageIs("OrganizationContext")
+ return &orgRepoContext{
+ OrgID: c.Org.Organization.ID,
+ Link: c.Org.OrgLink,
+ TmplList: tmplOrgSettingsWebhooks,
+ TmplNew: tmplOrgSettingsWebhookNew,
}, nil
}
- return nil, errors.New("Unable to set OrgRepo context")
+ return nil, errors.New("unable to determine context")
}
-func checkHookType(c *context.Context) string {
+func Webhooks(c *context.Context, orCtx *orgRepoContext) {
+ c.Title("repo.settings.hooks")
+ c.PageIs("SettingsHooks")
+ c.Data["Types"] = conf.Webhook.Types
+
+ var err error
+ var ws []*db.Webhook
+ if orCtx.RepoID > 0 {
+ c.Data["Description"] = c.Tr("repo.settings.hooks_desc")
+ ws, err = db.GetWebhooksByRepoID(orCtx.RepoID)
+ } else {
+ c.Data["Description"] = c.Tr("org.settings.hooks_desc")
+ ws, err = db.GetWebhooksByOrgID(orCtx.OrgID)
+ }
+ if err != nil {
+ c.Error(err, "get webhooks")
+ return
+ }
+ c.Data["Webhooks"] = ws
+
+ c.Success(orCtx.TmplList)
+}
+
+func WebhooksNew(c *context.Context, orCtx *orgRepoContext) {
+ c.Title("repo.settings.add_webhook")
+ c.PageIs("SettingsHooks")
+ c.PageIs("SettingsHooksNew")
+
+ allowed := false
hookType := strings.ToLower(c.Params(":type"))
- if !com.IsSliceContainsStr(conf.Webhook.Types, hookType) {
+ for _, typ := range conf.Webhook.Types {
+ if hookType == typ {
+ allowed = true
+ c.Data["HookType"] = typ
+ break
+ }
+ }
+ if !allowed {
c.NotFound()
- return ""
+ return
}
- return hookType
+
+ c.Success(orCtx.TmplNew)
}
-func WebhooksNew(c *context.Context) {
- c.Data["Title"] = c.Tr("repo.settings.add_webhook")
- c.Data["PageIsSettingsHooks"] = true
- c.Data["PageIsSettingsHooksNew"] = true
- c.Data["Webhook"] = db.Webhook{HookEvent: &db.HookEvent{}}
+var localHostnames = []string{
+ "localhost",
+ "127.0.0.1",
+ "::1",
+ "0:0:0:0:0:0:0:1",
+}
- orCtx, err := getOrgRepoCtx(c)
- if err != nil {
- c.Error(err, "get organization repository context")
+// isLocalHostname returns true if given hostname is a known local address.
+func isLocalHostname(hostname string) bool {
+ for _, local := range localHostnames {
+ if hostname == local {
+ return true
+ }
+ }
+ return false
+}
+
+func validateWebhook(actor *db.User, l macaron.Locale, w *db.Webhook) (field string, msg string, ok bool) {
+ if !actor.IsAdmin {
+ // 🚨 SECURITY: Local addresses must not be allowed by non-admins to prevent SSRF,
+ // see https://github.com/gogs/gogs/issues/5366 for details.
+ payloadURL, err := url.Parse(w.URL)
+ if err != nil {
+ return "PayloadURL", l.Tr("repo.settings.webhook.err_cannot_parse_payload_url", err), false
+ }
+
+ if isLocalHostname(payloadURL.Hostname()) {
+ return "PayloadURL", l.Tr("repo.settings.webhook.err_cannot_use_local_addresses"), false
+ }
+ }
+
+ return "", "", true
+}
+
+func validateAndCreateWebhook(c *context.Context, orCtx *orgRepoContext, w *db.Webhook) {
+ c.Data["Webhook"] = w
+
+ if c.HasError() {
+ c.Success(orCtx.TmplNew)
return
}
- c.Data["HookType"] = checkHookType(c)
- if c.Written() {
+ field, msg, ok := validateWebhook(c.User, c.Locale, w)
+ if !ok {
+ c.FormErr(field)
+ c.RenderWithErr(msg, orCtx.TmplNew, nil)
+ return
+ }
+
+ if err := w.UpdateEvent(); err != nil {
+ c.Error(err, "update event")
+ return
+ } else if err := db.CreateWebhook(w); err != nil {
+ c.Error(err, "create webhook")
return
}
- c.Data["BaseLink"] = orCtx.Link
- c.Success(orCtx.NewTemplate)
+ c.Flash.Success(c.Tr("repo.settings.add_hook_success"))
+ c.Redirect(orCtx.Link + "/settings/hooks")
}
-func ParseHookEvent(f form.Webhook) *db.HookEvent {
+func toHookEvent(f form.Webhook) *db.HookEvent {
return &db.HookEvent{
PushOnly: f.PushOnly(),
SendEverything: f.SendEverything(),
@@ -123,25 +197,12 @@ func ParseHookEvent(f form.Webhook) *db.HookEvent {
}
}
-func WebHooksNewPost(c *context.Context, f form.NewWebhook) {
- c.Data["Title"] = c.Tr("repo.settings.add_webhook")
- c.Data["PageIsSettingsHooks"] = true
- c.Data["PageIsSettingsHooksNew"] = true
- c.Data["Webhook"] = db.Webhook{HookEvent: &db.HookEvent{}}
+func WebhooksNewPost(c *context.Context, orCtx *orgRepoContext, f form.NewWebhook) {
+ c.Title("repo.settings.add_webhook")
+ c.PageIs("SettingsHooks")
+ c.PageIs("SettingsHooksNew")
c.Data["HookType"] = "gogs"
- orCtx, err := getOrgRepoCtx(c)
- if err != nil {
- c.Error(err, "get organization repository context")
- return
- }
- c.Data["BaseLink"] = orCtx.Link
-
- if c.HasError() {
- c.Success(orCtx.NewTemplate)
- return
- }
-
contentType := db.JSON
if db.HookContentType(f.ContentType) == db.FORM {
contentType = db.FORM
@@ -149,49 +210,32 @@ func WebHooksNewPost(c *context.Context, f form.NewWebhook) {
w := &db.Webhook{
RepoID: orCtx.RepoID,
+ OrgID: orCtx.OrgID,
URL: f.PayloadURL,
ContentType: contentType,
Secret: f.Secret,
- HookEvent: ParseHookEvent(f.Webhook),
+ HookEvent: toHookEvent(f.Webhook),
IsActive: f.Active,
HookTaskType: db.GOGS,
- OrgID: orCtx.OrgID,
}
- if err := w.UpdateEvent(); err != nil {
- c.Error(err, "update event")
- return
- } else if err := db.CreateWebhook(w); err != nil {
- c.Error(err, "create webhook")
- return
- }
-
- c.Flash.Success(c.Tr("repo.settings.add_hook_success"))
- c.Redirect(orCtx.Link + "/settings/hooks")
+ validateAndCreateWebhook(c, orCtx, w)
}
-func SlackHooksNewPost(c *context.Context, f form.NewSlackHook) {
- c.Data["Title"] = c.Tr("repo.settings")
- c.Data["PageIsSettingsHooks"] = true
- c.Data["PageIsSettingsHooksNew"] = true
- c.Data["Webhook"] = db.Webhook{HookEvent: &db.HookEvent{}}
-
- orCtx, err := getOrgRepoCtx(c)
- if err != nil {
- c.Error(err, "get organization repository context")
- return
- }
-
- if c.HasError() {
- c.Success(orCtx.NewTemplate)
- return
- }
+func WebhooksSlackNewPost(c *context.Context, orCtx *orgRepoContext, f form.NewSlackHook) {
+ c.Title("repo.settings.add_webhook")
+ c.PageIs("SettingsHooks")
+ c.PageIs("SettingsHooksNew")
+ c.Data["HookType"] = "slack"
- meta, err := jsoniter.Marshal(&db.SlackMeta{
+ meta := &db.SlackMeta{
Channel: f.Channel,
Username: f.Username,
IconURL: f.IconURL,
Color: f.Color,
- })
+ }
+ c.Data["SlackMeta"] = meta
+
+ p, err := jsoniter.Marshal(meta)
if err != nil {
c.Error(err, "marshal JSON")
return
@@ -201,47 +245,29 @@ func SlackHooksNewPost(c *context.Context, f form.NewSlackHook) {
RepoID: orCtx.RepoID,
URL: f.PayloadURL,
ContentType: db.JSON,
- HookEvent: ParseHookEvent(f.Webhook),
+ HookEvent: toHookEvent(f.Webhook),
IsActive: f.Active,
HookTaskType: db.SLACK,
- Meta: string(meta),
+ Meta: string(p),
OrgID: orCtx.OrgID,
}
- if err := w.UpdateEvent(); err != nil {
- c.Error(err, "update event")
- return
- } else if err := db.CreateWebhook(w); err != nil {
- c.Error(err, "create webhook")
- return
- }
-
- c.Flash.Success(c.Tr("repo.settings.add_hook_success"))
- c.Redirect(orCtx.Link + "/settings/hooks")
+ validateAndCreateWebhook(c, orCtx, w)
}
-// FIXME: merge logic to Slack
-func DiscordHooksNewPost(c *context.Context, f form.NewDiscordHook) {
- c.Data["Title"] = c.Tr("repo.settings")
- c.Data["PageIsSettingsHooks"] = true
- c.Data["PageIsSettingsHooksNew"] = true
- c.Data["Webhook"] = db.Webhook{HookEvent: &db.HookEvent{}}
+func WebhooksDiscordNewPost(c *context.Context, orCtx *orgRepoContext, f form.NewDiscordHook) {
+ c.Title("repo.settings.add_webhook")
+ c.PageIs("SettingsHooks")
+ c.PageIs("SettingsHooksNew")
+ c.Data["HookType"] = "discord"
- orCtx, err := getOrgRepoCtx(c)
- if err != nil {
- c.Error(err, "get organization repository context")
- return
- }
-
- if c.HasError() {
- c.Success(orCtx.NewTemplate)
- return
- }
-
- meta, err := jsoniter.Marshal(&db.SlackMeta{
+ meta := &db.SlackMeta{
Username: f.Username,
IconURL: f.IconURL,
Color: f.Color,
- })
+ }
+ c.Data["SlackMeta"] = meta
+
+ p, err := jsoniter.Marshal(meta)
if err != nil {
c.Error(err, "marshal JSON")
return
@@ -251,72 +277,37 @@ func DiscordHooksNewPost(c *context.Context, f form.NewDiscordHook) {
RepoID: orCtx.RepoID,
URL: f.PayloadURL,
ContentType: db.JSON,
- HookEvent: ParseHookEvent(f.Webhook),
+ HookEvent: toHookEvent(f.Webhook),
IsActive: f.Active,
HookTaskType: db.DISCORD,
- Meta: string(meta),
+ Meta: string(p),
OrgID: orCtx.OrgID,
}
- if err := w.UpdateEvent(); err != nil {
- c.Error(err, "update event")
- return
- } else if err := db.CreateWebhook(w); err != nil {
- c.Error(err, "create webhook")
- return
- }
-
- c.Flash.Success(c.Tr("repo.settings.add_hook_success"))
- c.Redirect(orCtx.Link + "/settings/hooks")
+ validateAndCreateWebhook(c, orCtx, w)
}
-func DingtalkHooksNewPost(c *context.Context, f form.NewDingtalkHook) {
- c.Data["Title"] = c.Tr("repo.settings")
- c.Data["PageIsSettingsHooks"] = true
- c.Data["PageIsSettingsHooksNew"] = true
- c.Data["Webhook"] = db.Webhook{HookEvent: &db.HookEvent{}}
-
- orCtx, err := getOrgRepoCtx(c)
- if err != nil {
- c.Error(err, "get organization repository context")
- return
- }
-
- if c.HasError() {
- c.Success(orCtx.NewTemplate)
- return
- }
+func WebhooksDingtalkNewPost(c *context.Context, orCtx *orgRepoContext, f form.NewDingtalkHook) {
+ c.Title("repo.settings.add_webhook")
+ c.PageIs("SettingsHooks")
+ c.PageIs("SettingsHooksNew")
+ c.Data["HookType"] = "dingtalk"
w := &db.Webhook{
RepoID: orCtx.RepoID,
URL: f.PayloadURL,
ContentType: db.JSON,
- HookEvent: ParseHookEvent(f.Webhook),
+ HookEvent: toHookEvent(f.Webhook),
IsActive: f.Active,
HookTaskType: db.DINGTALK,
OrgID: orCtx.OrgID,
}
- if err := w.UpdateEvent(); err != nil {
- c.Error(err, "update event")
- return
- } else if err := db.CreateWebhook(w); err != nil {
- c.Error(err, "create webhook")
- return
- }
-
- c.Flash.Success(c.Tr("repo.settings.add_hook_success"))
- c.Redirect(orCtx.Link + "/settings/hooks")
+ validateAndCreateWebhook(c, orCtx, w)
}
-func checkWebhook(c *context.Context) (*OrgRepoCtx, *db.Webhook) {
- c.Data["RequireHighlightJS"] = true
-
- orCtx, err := getOrgRepoCtx(c)
- if err != nil {
- c.Error(err, "get organization repository context")
- return nil, nil
- }
- c.Data["BaseLink"] = orCtx.Link
+func loadWebhook(c *context.Context, orCtx *orgRepoContext) *db.Webhook {
+ c.RequireHighlightJS()
+ var err error
var w *db.Webhook
if orCtx.RepoID > 0 {
w, err = db.GetWebhookOfRepoByID(c.Repo.Repository.ID, c.ParamsInt64(":id"))
@@ -325,70 +316,61 @@ func checkWebhook(c *context.Context) (*OrgRepoCtx, *db.Webhook) {
}
if err != nil {
c.NotFoundOrError(err, "get webhook")
- return nil, nil
+ return nil
}
+ c.Data["Webhook"] = w
switch w.HookTaskType {
case db.SLACK:
- c.Data["SlackHook"] = w.GetSlackHook()
+ c.Data["SlackMeta"] = w.SlackMeta()
c.Data["HookType"] = "slack"
case db.DISCORD:
- c.Data["SlackHook"] = w.GetSlackHook()
+ c.Data["SlackMeta"] = w.SlackMeta()
c.Data["HookType"] = "discord"
case db.DINGTALK:
c.Data["HookType"] = "dingtalk"
default:
c.Data["HookType"] = "gogs"
}
+ c.Data["FormURL"] = fmt.Sprintf("%s/settings/hooks/%s/%d", orCtx.Link, c.Data["HookType"], w.ID)
+ c.Data["DeleteURL"] = fmt.Sprintf("%s/settings/hooks/delete", orCtx.Link)
c.Data["History"], err = w.History(1)
if err != nil {
c.Error(err, "get history")
- return nil, nil
+ return nil
}
- return orCtx, w
+ return w
}
-func WebHooksEdit(c *context.Context) {
- c.Data["Title"] = c.Tr("repo.settings.update_webhook")
- c.Data["PageIsSettingsHooks"] = true
- c.Data["PageIsSettingsHooksEdit"] = true
+func WebhooksEdit(c *context.Context, orCtx *orgRepoContext) {
+ c.Title("repo.settings.update_webhook")
+ c.PageIs("SettingsHooks")
+ c.PageIs("SettingsHooksEdit")
- orCtx, w := checkWebhook(c)
+ loadWebhook(c, orCtx)
if c.Written() {
return
}
- c.Data["Webhook"] = w
- c.Success(orCtx.NewTemplate)
+ c.Success(orCtx.TmplNew)
}
-func WebHooksEditPost(c *context.Context, f form.NewWebhook) {
- c.Data["Title"] = c.Tr("repo.settings.update_webhook")
- c.Data["PageIsSettingsHooks"] = true
- c.Data["PageIsSettingsHooksEdit"] = true
-
- orCtx, w := checkWebhook(c)
- if c.Written() {
- return
- }
+func validateAndUpdateWebhook(c *context.Context, orCtx *orgRepoContext, w *db.Webhook) {
c.Data["Webhook"] = w
if c.HasError() {
- c.Success(orCtx.NewTemplate)
+ c.Success(orCtx.TmplNew)
return
}
- contentType := db.JSON
- if db.HookContentType(f.ContentType) == db.FORM {
- contentType = db.FORM
+ field, msg, ok := validateWebhook(c.User, c.Locale, w)
+ if !ok {
+ c.FormErr(field)
+ c.RenderWithErr(msg, orCtx.TmplNew, nil)
+ return
}
- w.URL = f.PayloadURL
- w.ContentType = contentType
- w.Secret = f.Secret
- w.HookEvent = ParseHookEvent(f.Webhook)
- w.IsActive = f.Active
if err := w.UpdateEvent(); err != nil {
c.Error(err, "update event")
return
@@ -401,19 +383,36 @@ func WebHooksEditPost(c *context.Context, f form.NewWebhook) {
c.Redirect(fmt.Sprintf("%s/settings/hooks/%d", orCtx.Link, w.ID))
}
-func SlackHooksEditPost(c *context.Context, f form.NewSlackHook) {
- c.Data["Title"] = c.Tr("repo.settings")
- c.Data["PageIsSettingsHooks"] = true
- c.Data["PageIsSettingsHooksEdit"] = true
+func WebhooksEditPost(c *context.Context, orCtx *orgRepoContext, f form.NewWebhook) {
+ c.Title("repo.settings.update_webhook")
+ c.PageIs("SettingsHooks")
+ c.PageIs("SettingsHooksEdit")
- orCtx, w := checkWebhook(c)
+ w := loadWebhook(c, orCtx)
if c.Written() {
return
}
- c.Data["Webhook"] = w
- if c.HasError() {
- c.Success(orCtx.NewTemplate)
+ contentType := db.JSON
+ if db.HookContentType(f.ContentType) == db.FORM {
+ contentType = db.FORM
+ }
+
+ w.URL = f.PayloadURL
+ w.ContentType = contentType
+ w.Secret = f.Secret
+ w.HookEvent = toHookEvent(f.Webhook)
+ w.IsActive = f.Active
+ validateAndUpdateWebhook(c, orCtx, w)
+}
+
+func WebhooksSlackEditPost(c *context.Context, orCtx *orgRepoContext, f form.NewSlackHook) {
+ c.Title("repo.settings.update_webhook")
+ c.PageIs("SettingsHooks")
+ c.PageIs("SettingsHooksEdit")
+
+ w := loadWebhook(c, orCtx)
+ if c.Written() {
return
}
@@ -430,36 +429,20 @@ func SlackHooksEditPost(c *context.Context, f form.NewSlackHook) {
w.URL = f.PayloadURL
w.Meta = string(meta)
- w.HookEvent = ParseHookEvent(f.Webhook)
+ w.HookEvent = toHookEvent(f.Webhook)
w.IsActive = f.Active
- if err := w.UpdateEvent(); err != nil {
- c.Error(err, "update event")
- return
- } else if err := db.UpdateWebhook(w); err != nil {
- c.Error(err, "update webhook")
- return
- }
-
- c.Flash.Success(c.Tr("repo.settings.update_hook_success"))
- c.Redirect(fmt.Sprintf("%s/settings/hooks/%d", orCtx.Link, w.ID))
+ validateAndUpdateWebhook(c, orCtx, w)
}
-// FIXME: merge logic to Slack
-func DiscordHooksEditPost(c *context.Context, f form.NewDiscordHook) {
- c.Data["Title"] = c.Tr("repo.settings")
- c.Data["PageIsSettingsHooks"] = true
- c.Data["PageIsSettingsHooksEdit"] = true
+func WebhooksDiscordEditPost(c *context.Context, orCtx *orgRepoContext, f form.NewDiscordHook) {
+ c.Title("repo.settings.update_webhook")
+ c.PageIs("SettingsHooks")
+ c.PageIs("SettingsHooksEdit")
- orCtx, w := checkWebhook(c)
+ w := loadWebhook(c, orCtx)
if c.Written() {
return
}
- c.Data["Webhook"] = w
-
- if c.HasError() {
- c.Success(orCtx.NewTemplate)
- return
- }
meta, err := jsoniter.Marshal(&db.SlackMeta{
Username: f.Username,
@@ -473,53 +456,28 @@ func DiscordHooksEditPost(c *context.Context, f form.NewDiscordHook) {
w.URL = f.PayloadURL
w.Meta = string(meta)
- w.HookEvent = ParseHookEvent(f.Webhook)
+ w.HookEvent = toHookEvent(f.Webhook)
w.IsActive = f.Active
- if err := w.UpdateEvent(); err != nil {
- c.Error(err, "update event")
- return
- } else if err := db.UpdateWebhook(w); err != nil {
- c.Error(err, "update webhook")
- return
- }
-
- c.Flash.Success(c.Tr("repo.settings.update_hook_success"))
- c.Redirect(fmt.Sprintf("%s/settings/hooks/%d", orCtx.Link, w.ID))
+ validateAndUpdateWebhook(c, orCtx, w)
}
-func DingtalkHooksEditPost(c *context.Context, f form.NewDingtalkHook) {
- c.Data["Title"] = c.Tr("repo.settings")
- c.Data["PageIsSettingsHooks"] = true
- c.Data["PageIsSettingsHooksEdit"] = true
+func WebhooksDingtalkEditPost(c *context.Context, orCtx *orgRepoContext, f form.NewDingtalkHook) {
+ c.Title("repo.settings.update_webhook")
+ c.PageIs("SettingsHooks")
+ c.PageIs("SettingsHooksEdit")
- orCtx, w := checkWebhook(c)
+ w := loadWebhook(c, orCtx)
if c.Written() {
return
}
- c.Data["Webhook"] = w
-
- if c.HasError() {
- c.Success(orCtx.NewTemplate)
- return
- }
w.URL = f.PayloadURL
- w.HookEvent = ParseHookEvent(f.Webhook)
+ w.HookEvent = toHookEvent(f.Webhook)
w.IsActive = f.Active
- if err := w.UpdateEvent(); err != nil {
- c.Error(err, "update event")
- return
- } else if err := db.UpdateWebhook(w); err != nil {
- c.Error(err, "update webhook")
- return
- }
-
- c.Flash.Success(c.Tr("repo.settings.update_hook_success"))
- c.Redirect(fmt.Sprintf("%s/settings/hooks/%d", orCtx.Link, w.ID))
+ validateAndUpdateWebhook(c, orCtx, w)
}
func TestWebhook(c *context.Context) {
-
var (
commitID string
commitMessage string
@@ -634,14 +592,20 @@ func RedeliveryWebhook(c *context.Context) {
c.Status(http.StatusOK)
}
-func DeleteWebhook(c *context.Context) {
- if err := db.DeleteWebhookOfRepoByID(c.Repo.Repository.ID, c.QueryInt64("id")); err != nil {
- c.Flash.Error("DeleteWebhookByRepoID: " + err.Error())
+func DeleteWebhook(c *context.Context, orCtx *orgRepoContext) {
+ var err error
+ if orCtx.RepoID > 0 {
+ err = db.DeleteWebhookOfRepoByID(orCtx.RepoID, c.QueryInt64("id"))
} else {
- c.Flash.Success(c.Tr("repo.settings.webhook_deletion_success"))
+ err = db.DeleteWebhookOfOrgByID(orCtx.OrgID, c.QueryInt64("id"))
+ }
+ if err != nil {
+ c.Error(err, "delete webhook")
+ return
}
+ c.Flash.Success(c.Tr("repo.settings.webhook_deletion_success"))
c.JSONSuccess(map[string]interface{}{
- "redirect": c.Repo.RepoLink + "/settings/hooks",
+ "redirect": orCtx.Link + "/settings/hooks",
})
}
diff --git a/internal/route/repo/webhook_test.go b/internal/route/repo/webhook_test.go
new file mode 100644
index 00000000..02f27b92
--- /dev/null
+++ b/internal/route/repo/webhook_test.go
@@ -0,0 +1,72 @@
+// Copyright 2020 The Gogs 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 repo
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+
+ "gogs.io/gogs/internal/db"
+ "gogs.io/gogs/internal/mock"
+)
+
+func Test_isLocalHostname(t *testing.T) {
+ tests := []struct {
+ hostname string
+ want bool
+ }{
+ {hostname: "localhost", want: true},
+ {hostname: "127.0.0.1", want: true},
+ {hostname: "::1", want: true},
+ {hostname: "0:0:0:0:0:0:0:1", want: true},
+
+ {hostname: "gogs.io", want: false},
+ }
+ for _, test := range tests {
+ t.Run("", func(t *testing.T) {
+ assert.Equal(t, test.want, isLocalHostname(test.hostname))
+ })
+ }
+}
+
+func Test_validateWebhook(t *testing.T) {
+ l := mock.NewLocale("en", func(s string, _ ...interface{}) string {
+ return s
+ })
+
+ tests := []struct {
+ name string
+ actor *db.User
+ webhook *db.Webhook
+ expField string
+ expMsg string
+ expOK bool
+ }{
+ {
+ name: "admin bypass local address check",
+ actor: &db.User{IsAdmin: true},
+ webhook: &db.Webhook{URL: "http://localhost:3306"},
+ expOK: true,
+ },
+
+ {
+ name: "local address not allowed",
+ actor: &db.User{},
+ webhook: &db.Webhook{URL: "http://localhost:3306"},
+ expField: "PayloadURL",
+ expMsg: "repo.settings.webhook.err_cannot_use_local_addresses",
+ expOK: false,
+ },
+ }
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ field, msg, ok := validateWebhook(test.actor, l, test.webhook)
+ assert.Equal(t, test.expOK, ok)
+ assert.Equal(t, test.expMsg, msg)
+ assert.Equal(t, test.expField, field)
+ })
+ }
+}