From a4676db7dd654705214b2568c2def8c8bcfb9777 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Mon, 3 Feb 2025 19:25:59 -0800 Subject: [PATCH 1/2] Fix commit status events (#33320) Fix #32873 Fix #33201 ~Fix #33244~ ~Fix #33302~ depends on ~#33396~ A part of this PR should be backported to v1.23 manually. --- options/locale/locale_en-US.ini | 2 + routers/web/repo/setting/webhook.go | 1 + services/forms/repo_form.go | 1 + services/webhook/dingtalk.go | 6 +++ services/webhook/discord.go | 6 +++ services/webhook/feishu.go | 6 +++ services/webhook/general.go | 12 ++++++ services/webhook/matrix.go | 7 ++++ services/webhook/msteams.go | 14 +++++++ services/webhook/packagist.go | 4 ++ services/webhook/payloader.go | 3 ++ services/webhook/slack.go | 6 +++ services/webhook/telegram.go | 6 +++ services/webhook/wechatwork.go | 6 +++ templates/repo/settings/webhook/settings.tmpl | 11 ++++++ tests/integration/repo_webhook_test.go | 39 +++++++++++++++++++ 16 files changed, 130 insertions(+) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 886628e4ff29a..2842ad16e7da0 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -2331,6 +2331,8 @@ settings.event_fork = Fork settings.event_fork_desc = Repository forked. settings.event_wiki = Wiki settings.event_wiki_desc = Wiki page created, renamed, edited or deleted. +settings.event_statuses = Statuses +settings.event_statuses_desc = Commit Status updated from the API. settings.event_release = Release settings.event_release_desc = Release published, updated or deleted in a repository. settings.event_push = Push diff --git a/routers/web/repo/setting/webhook.go b/routers/web/repo/setting/webhook.go index 997145b5071a8..4ff24670412da 100644 --- a/routers/web/repo/setting/webhook.go +++ b/routers/web/repo/setting/webhook.go @@ -184,6 +184,7 @@ func ParseHookEvent(form forms.WebhookForm) *webhook_module.HookEvent { webhook_module.HookEventWiki: form.Wiki, webhook_module.HookEventRepository: form.Repository, webhook_module.HookEventPackage: form.Package, + webhook_module.HookEventStatus: form.Status, }, BranchFilter: form.BranchFilter, } diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index 2c6373e03cf8c..70019f3fa907e 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -243,6 +243,7 @@ type WebhookForm struct { Repository bool Release bool Package bool + Status bool Active bool BranchFilter string `binding:"GlobPattern"` AuthorizationHeader string diff --git a/services/webhook/dingtalk.go b/services/webhook/dingtalk.go index 992b8c566fad7..3ea8f50764d62 100644 --- a/services/webhook/dingtalk.go +++ b/services/webhook/dingtalk.go @@ -170,6 +170,12 @@ func (dc dingtalkConvertor) Package(p *api.PackagePayload) (DingtalkPayload, err return createDingtalkPayload(text, text, "view package", p.Package.HTMLURL), nil } +func (dc dingtalkConvertor) Status(p *api.CommitStatusPayload) (DingtalkPayload, error) { + text, _ := getStatusPayloadInfo(p, noneLinkFormatter, true) + + return createDingtalkPayload(text, text, "Status Changed", p.TargetURL), nil +} + func createDingtalkPayload(title, text, singleTitle, singleURL string) DingtalkPayload { return DingtalkPayload{ MsgType: "actionCard", diff --git a/services/webhook/discord.go b/services/webhook/discord.go index 30d930062eb4c..43e5e533bff65 100644 --- a/services/webhook/discord.go +++ b/services/webhook/discord.go @@ -265,6 +265,12 @@ func (d discordConvertor) Package(p *api.PackagePayload) (DiscordPayload, error) return d.createPayload(p.Sender, text, "", p.Package.HTMLURL, color), nil } +func (d discordConvertor) Status(p *api.CommitStatusPayload) (DiscordPayload, error) { + text, color := getStatusPayloadInfo(p, noneLinkFormatter, false) + + return d.createPayload(p.Sender, text, "", p.TargetURL, color), nil +} + func newDiscordRequest(_ context.Context, w *webhook_model.Webhook, t *webhook_model.HookTask) (*http.Request, []byte, error) { meta := &DiscordMeta{} if err := json.Unmarshal([]byte(w.Meta), meta); err != nil { diff --git a/services/webhook/feishu.go b/services/webhook/feishu.go index 4e6aebc39dc82..639118d2a5eee 100644 --- a/services/webhook/feishu.go +++ b/services/webhook/feishu.go @@ -166,6 +166,12 @@ func (fc feishuConvertor) Package(p *api.PackagePayload) (FeishuPayload, error) return newFeishuTextPayload(text), nil } +func (fc feishuConvertor) Status(p *api.CommitStatusPayload) (FeishuPayload, error) { + text, _ := getStatusPayloadInfo(p, noneLinkFormatter, true) + + return newFeishuTextPayload(text), nil +} + func newFeishuRequest(_ context.Context, w *webhook_model.Webhook, t *webhook_model.HookTask) (*http.Request, []byte, error) { var pc payloadConvertor[FeishuPayload] = feishuConvertor{} return newJSONRequest(pc, w, t, true) diff --git a/services/webhook/general.go b/services/webhook/general.go index dde43bb3495c6..91bf68600fcc5 100644 --- a/services/webhook/general.go +++ b/services/webhook/general.go @@ -307,6 +307,18 @@ func getPackagePayloadInfo(p *api.PackagePayload, linkFormatter linkFormatter, w return text, color } +func getStatusPayloadInfo(p *api.CommitStatusPayload, linkFormatter linkFormatter, withSender bool) (text string, color int) { + refLink := linkFormatter(p.TargetURL, p.Context+"["+p.SHA+"]:"+p.Description) + + text = fmt.Sprintf("Commit Status changed: %s", refLink) + color = greenColor + if withSender { + text += fmt.Sprintf(" by %s", linkFormatter(setting.AppURL+url.PathEscape(p.Sender.UserName), p.Sender.UserName)) + } + + return text, color +} + // ToHook convert models.Webhook to api.Hook // This function is not part of the convert package to prevent an import cycle func ToHook(repoLink string, w *webhook_model.Webhook) (*api.Hook, error) { diff --git a/services/webhook/matrix.go b/services/webhook/matrix.go index 96dfa139aca46..ec21712837b93 100644 --- a/services/webhook/matrix.go +++ b/services/webhook/matrix.go @@ -244,6 +244,13 @@ func (m matrixConvertor) Package(p *api.PackagePayload) (MatrixPayload, error) { return m.newPayload(text) } +func (m matrixConvertor) Status(p *api.CommitStatusPayload) (MatrixPayload, error) { + refLink := htmlLinkFormatter(p.TargetURL, p.Context+"["+p.SHA+"]:"+p.Description) + text := fmt.Sprintf("Commit Status changed: %s", refLink) + + return m.newPayload(text) +} + var urlRegex = regexp.MustCompile(`]*?href="([^">]*?)">(.*?)`) func getMessageBody(htmlText string) string { diff --git a/services/webhook/msteams.go b/services/webhook/msteams.go index 1ae7c4f93155a..485f695be2042 100644 --- a/services/webhook/msteams.go +++ b/services/webhook/msteams.go @@ -303,6 +303,20 @@ func (m msteamsConvertor) Package(p *api.PackagePayload) (MSTeamsPayload, error) ), nil } +func (m msteamsConvertor) Status(p *api.CommitStatusPayload) (MSTeamsPayload, error) { + title, color := getStatusPayloadInfo(p, noneLinkFormatter, false) + + return createMSTeamsPayload( + p.Repo, + p.Sender, + title, + "", + p.TargetURL, + color, + &MSTeamsFact{"CommitStatus:", p.Context}, + ), nil +} + func createMSTeamsPayload(r *api.Repository, s *api.User, title, text, actionTarget string, color int, fact *MSTeamsFact) MSTeamsPayload { facts := make([]MSTeamsFact, 0, 2) if r != nil { diff --git a/services/webhook/packagist.go b/services/webhook/packagist.go index e66895832b922..6864fc822abde 100644 --- a/services/webhook/packagist.go +++ b/services/webhook/packagist.go @@ -110,6 +110,10 @@ func (pc packagistConvertor) Package(_ *api.PackagePayload) (PackagistPayload, e return PackagistPayload{}, nil } +func (pc packagistConvertor) Status(_ *api.CommitStatusPayload) (PackagistPayload, error) { + return PackagistPayload{}, nil +} + func newPackagistRequest(_ context.Context, w *webhook_model.Webhook, t *webhook_model.HookTask) (*http.Request, []byte, error) { meta := &PackagistMeta{} if err := json.Unmarshal([]byte(w.Meta), meta); err != nil { diff --git a/services/webhook/payloader.go b/services/webhook/payloader.go index ab280a25b661e..c29ad8ac9206e 100644 --- a/services/webhook/payloader.go +++ b/services/webhook/payloader.go @@ -28,6 +28,7 @@ type payloadConvertor[T any] interface { Release(*api.ReleasePayload) (T, error) Wiki(*api.WikiPayload) (T, error) Package(*api.PackagePayload) (T, error) + Status(*api.CommitStatusPayload) (T, error) } func convertUnmarshalledJSON[T, P any](convert func(P) (T, error), data []byte) (t T, err error) { @@ -77,6 +78,8 @@ func newPayload[T any](rc payloadConvertor[T], data []byte, event webhook_module return convertUnmarshalledJSON(rc.Wiki, data) case webhook_module.HookEventPackage: return convertUnmarshalledJSON(rc.Package, data) + case webhook_module.HookEventStatus: + return convertUnmarshalledJSON(rc.Status, data) } return t, fmt.Errorf("newPayload unsupported event: %s", event) } diff --git a/services/webhook/slack.go b/services/webhook/slack.go index 0371ee23e6309..80ed747fd1f2f 100644 --- a/services/webhook/slack.go +++ b/services/webhook/slack.go @@ -167,6 +167,12 @@ func (s slackConvertor) Package(p *api.PackagePayload) (SlackPayload, error) { return s.createPayload(text, nil), nil } +func (s slackConvertor) Status(p *api.CommitStatusPayload) (SlackPayload, error) { + text, _ := getStatusPayloadInfo(p, SlackLinkFormatter, true) + + return s.createPayload(text, nil), nil +} + // Push implements payloadConvertor Push method func (s slackConvertor) Push(p *api.PushPayload) (SlackPayload, error) { // n new commits diff --git a/services/webhook/telegram.go b/services/webhook/telegram.go index 6fbf995801cbd..485e2d990bae7 100644 --- a/services/webhook/telegram.go +++ b/services/webhook/telegram.go @@ -174,6 +174,12 @@ func (t telegramConvertor) Package(p *api.PackagePayload) (TelegramPayload, erro return createTelegramPayloadHTML(text), nil } +func (t telegramConvertor) Status(p *api.CommitStatusPayload) (TelegramPayload, error) { + text, _ := getStatusPayloadInfo(p, htmlLinkFormatter, true) + + return createTelegramPayloadHTML(text), nil +} + func createTelegramPayloadHTML(msgHTML string) TelegramPayload { // https://core.telegram.org/bots/api#formatting-options return TelegramPayload{ diff --git a/services/webhook/wechatwork.go b/services/webhook/wechatwork.go index 44e0ff7de51e5..1c834b4020aee 100644 --- a/services/webhook/wechatwork.go +++ b/services/webhook/wechatwork.go @@ -175,6 +175,12 @@ func (wc wechatworkConvertor) Package(p *api.PackagePayload) (WechatworkPayload, return newWechatworkMarkdownPayload(text), nil } +func (wc wechatworkConvertor) Status(p *api.CommitStatusPayload) (WechatworkPayload, error) { + text, _ := getStatusPayloadInfo(p, noneLinkFormatter, true) + + return newWechatworkMarkdownPayload(text), nil +} + func newWechatworkRequest(_ context.Context, w *webhook_model.Webhook, t *webhook_model.HookTask) (*http.Request, []byte, error) { var pc payloadConvertor[WechatworkPayload] = wechatworkConvertor{} return newJSONRequest(pc, w, t, true) diff --git a/templates/repo/settings/webhook/settings.tmpl b/templates/repo/settings/webhook/settings.tmpl index 1a01a6aea8c27..3b28a4c6c01e3 100644 --- a/templates/repo/settings/webhook/settings.tmpl +++ b/templates/repo/settings/webhook/settings.tmpl @@ -109,6 +109,17 @@ + +
+
+
+ + + {{ctx.Locale.Tr "repo.settings.event_statuses_desc"}} +
+
+
+
diff --git a/tests/integration/repo_webhook_test.go b/tests/integration/repo_webhook_test.go index 17905513c37e0..2f9a815fef4a7 100644 --- a/tests/integration/repo_webhook_test.go +++ b/tests/integration/repo_webhook_test.go @@ -16,6 +16,7 @@ import ( auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" + "code.gitea.io/gitea/models/webhook" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/json" api "code.gitea.io/gitea/modules/structs" @@ -66,6 +67,19 @@ func testAPICreateWebhookForRepo(t *testing.T, session *TestSession, userName, r MakeRequest(t, req, http.StatusCreated) } +func testCreateWebhookForRepo(t *testing.T, session *TestSession, webhookType, userName, repoName, url, eventKind string) { + csrf := GetUserCSRFToken(t, session) + req := NewRequestWithValues(t, "POST", "/"+userName+"/"+repoName+"/settings/hooks/"+webhookType+"/new", map[string]string{ + "_csrf": csrf, + "payload_url": url, + "events": eventKind, + "active": "true", + "content_type": fmt.Sprintf("%d", webhook.ContentTypeJSON), + "http_method": "POST", + }) + session.MakeRequest(t, req, http.StatusSeeOther) +} + func testAPICreateWebhookForOrg(t *testing.T, session *TestSession, userName, url, event string) { token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAll) req := NewRequestWithJSON(t, "POST", "/api/v1/orgs/"+userName+"/hooks", api.CreateHookOption{ @@ -562,3 +576,28 @@ func Test_WebhookStatus(t *testing.T) { assert.EqualValues(t, commitID, payloads[0].SHA) }) } + +func Test_WebhookStatus_NoWrongTrigger(t *testing.T) { + var trigger string + provider := newMockWebhookProvider(func(r *http.Request) { + assert.NotContains(t, r.Header["X-Github-Event-Type"], "status", "X-GitHub-Event-Type should not contain status") + assert.NotContains(t, r.Header["X-Gitea-Event-Type"], "status", "X-Gitea-Event-Type should not contain status") + assert.NotContains(t, r.Header["X-Gogs-Event-Type"], "status", "X-Gogs-Event-Type should not contain status") + trigger = "push" + }, http.StatusOK) + defer provider.Close() + + onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { + // 1. create a new webhook with special webhook for repo1 + session := loginUser(t, "user2") + + // create a push_only webhook from web UI + testCreateWebhookForRepo(t, session, "gitea", "user2", "repo1", provider.URL(), "push_only") + + // 2. trigger the webhook with a push action + testCreateFile(t, session, "user2", "repo1", "master", "test_webhook_push.md", "# a test file for webhook push") + + // 3. validate the webhook is triggered with right event + assert.EqualValues(t, "push", trigger) + }) +} From d0f4e92563e3486f87c2758c76bd774bdbc63738 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrique=20Corr=C3=AAa?= <75134774+HeCorr@users.noreply.github.com> Date: Tue, 4 Feb 2025 02:21:23 -0300 Subject: [PATCH 2/2] Reject star-related requests if stars are disabled (#33208) This PR fixes #33205. If stars are disabled: * The `.../repo/stars` page returns a 403 Forbidden error * Star-related API endpoints return a 403 Forbidden error saying `Stars are disabled.` * Same for action endpoints --------- Co-authored-by: wxiaoguang --- routers/api/v1/api.go | 16 +++++-- routers/api/v1/repo/star.go | 2 + routers/api/v1/user/star.go | 8 ++++ routers/web/web.go | 13 ++++- templates/swagger/v1_json.tmpl | 15 ++++++ tests/integration/api_user_star_test.go | 64 +++++++++++++++++++++++++ 6 files changed, 113 insertions(+), 5 deletions(-) diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 438db4ae71b90..0aa38b8b6abbb 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -580,6 +580,16 @@ func reqWebhooksEnabled() func(ctx *context.APIContext) { } } +// reqStarsEnabled requires Starring to be enabled in the config. +func reqStarsEnabled() func(ctx *context.APIContext) { + return func(ctx *context.APIContext) { + if setting.Repository.DisableStars { + ctx.Error(http.StatusForbidden, "", "stars disabled by administrator") + return + } + } +} + func orgAssignment(args ...bool) func(ctx *context.APIContext) { var ( assignOrg bool @@ -995,7 +1005,7 @@ func Routes() *web.Router { m.Get("/{target}", user.CheckFollowing) }) - m.Get("/starred", user.GetStarredRepos) + m.Get("/starred", reqStarsEnabled(), user.GetStarredRepos) m.Get("/subscriptions", user.GetWatchedRepos) }, context.UserAssignmentAPI(), checkTokenPublicOnly()) @@ -1086,7 +1096,7 @@ func Routes() *web.Router { m.Put("", user.Star) m.Delete("", user.Unstar) }, repoAssignment(), checkTokenPublicOnly()) - }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository)) + }, reqStarsEnabled(), tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository)) m.Get("/times", repo.ListMyTrackedTimes) m.Get("/stopwatches", repo.GetStopwatches) m.Get("/subscriptions", user.GetMyWatchedRepos) @@ -1248,7 +1258,7 @@ func Routes() *web.Router { m.Post("/markup", reqToken(), bind(api.MarkupOption{}), misc.Markup) m.Post("/markdown", reqToken(), bind(api.MarkdownOption{}), misc.Markdown) m.Post("/markdown/raw", reqToken(), misc.MarkdownRaw) - m.Get("/stargazers", repo.ListStargazers) + m.Get("/stargazers", reqStarsEnabled(), repo.ListStargazers) m.Get("/subscribers", repo.ListSubscribers) m.Group("/subscription", func() { m.Get("", user.IsWatching) diff --git a/routers/api/v1/repo/star.go b/routers/api/v1/repo/star.go index 99676de119c1f..46ed17ad91eb6 100644 --- a/routers/api/v1/repo/star.go +++ b/routers/api/v1/repo/star.go @@ -44,6 +44,8 @@ func ListStargazers(ctx *context.APIContext) { // "$ref": "#/responses/UserList" // "404": // "$ref": "#/responses/notFound" + // "403": + // "$ref": "#/responses/forbidden" stargazers, err := repo_model.GetStargazers(ctx, ctx.Repo.Repository, utils.GetListOptions(ctx)) if err != nil { diff --git a/routers/api/v1/user/star.go b/routers/api/v1/user/star.go index ad9ed9548d091..70e54bc1ae4d9 100644 --- a/routers/api/v1/user/star.go +++ b/routers/api/v1/user/star.go @@ -66,6 +66,8 @@ func GetStarredRepos(ctx *context.APIContext) { // "$ref": "#/responses/RepositoryList" // "404": // "$ref": "#/responses/notFound" + // "403": + // "$ref": "#/responses/forbidden" private := ctx.ContextUser.ID == ctx.Doer.ID repos, err := getStarredRepos(ctx, ctx.ContextUser, private) @@ -97,6 +99,8 @@ func GetMyStarredRepos(ctx *context.APIContext) { // responses: // "200": // "$ref": "#/responses/RepositoryList" + // "403": + // "$ref": "#/responses/forbidden" repos, err := getStarredRepos(ctx, ctx.Doer, true) if err != nil { @@ -128,6 +132,8 @@ func IsStarring(ctx *context.APIContext) { // "$ref": "#/responses/empty" // "404": // "$ref": "#/responses/notFound" + // "403": + // "$ref": "#/responses/forbidden" if repo_model.IsStaring(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID) { ctx.Status(http.StatusNoContent) @@ -193,6 +199,8 @@ func Unstar(ctx *context.APIContext) { // "$ref": "#/responses/empty" // "404": // "$ref": "#/responses/notFound" + // "403": + // "$ref": "#/responses/forbidden" err := repo_model.StarRepo(ctx, ctx.Doer, ctx.Repo.Repository, false) if err != nil { diff --git a/routers/web/web.go b/routers/web/web.go index f772f6dbb979d..3cb6dc2551f71 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -347,6 +347,13 @@ func registerRoutes(m *web.Router) { } } + starsEnabled := func(ctx *context.Context) { + if setting.Repository.DisableStars { + ctx.Error(http.StatusForbidden) + return + } + } + lfsServerEnabled := func(ctx *context.Context) { if !setting.LFS.StartServer { ctx.Error(http.StatusNotFound) @@ -1593,10 +1600,12 @@ func registerRoutes(m *web.Router) { // end "/{username}/{reponame}": repo code m.Group("/{username}/{reponame}", func() { - m.Get("/stars", repo.Stars) + m.Get("/stars", starsEnabled, repo.Stars) m.Get("/watchers", repo.Watchers) m.Get("/search", reqUnitCodeReader, repo.Search) - m.Post("/action/{action}", reqSignIn, repo.Action) + m.Post("/action/{action:star|unstar}", reqSignIn, starsEnabled, repo.Action) + m.Post("/action/{action:watch|unwatch}", reqSignIn, repo.Action) + m.Post("/action/{action:accept_transfer|reject_transfer}", reqSignIn, repo.Action) }, optSignIn, context.RepoAssignment) common.AddOwnerRepoGitLFSRoutes(m, optSignInIgnoreCsrf, lfsServerEnabled) // "/{username}/{reponame}/{lfs-paths}": git-lfs support diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index c58b21062d85a..d22e01c787619 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -13808,6 +13808,9 @@ "200": { "$ref": "#/responses/UserList" }, + "403": { + "$ref": "#/responses/forbidden" + }, "404": { "$ref": "#/responses/notFound" } @@ -17546,6 +17549,9 @@ "responses": { "200": { "$ref": "#/responses/RepositoryList" + }, + "403": { + "$ref": "#/responses/forbidden" } } } @@ -17577,6 +17583,9 @@ "204": { "$ref": "#/responses/empty" }, + "403": { + "$ref": "#/responses/forbidden" + }, "404": { "$ref": "#/responses/notFound" } @@ -17642,6 +17651,9 @@ "204": { "$ref": "#/responses/empty" }, + "403": { + "$ref": "#/responses/forbidden" + }, "404": { "$ref": "#/responses/notFound" } @@ -18318,6 +18330,9 @@ "200": { "$ref": "#/responses/RepositoryList" }, + "403": { + "$ref": "#/responses/forbidden" + }, "404": { "$ref": "#/responses/notFound" } diff --git a/tests/integration/api_user_star_test.go b/tests/integration/api_user_star_test.go index 0062889a92db6..368756528a8f8 100644 --- a/tests/integration/api_user_star_test.go +++ b/tests/integration/api_user_star_test.go @@ -11,7 +11,9 @@ import ( auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/test" "code.gitea.io/gitea/tests" "github.com/stretchr/testify/assert" @@ -91,3 +93,65 @@ func TestAPIStar(t *testing.T) { MakeRequest(t, req, http.StatusNoContent) }) } + +func TestAPIStarDisabled(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + user := "user1" + repo := "user2/repo1" + + session := loginUser(t, user) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadUser) + tokenWithUserScope := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteUser, auth_model.AccessTokenScopeWriteRepository) + + defer test.MockVariableValue(&setting.Repository.DisableStars, true)() + + t.Run("Star", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + req := NewRequest(t, "PUT", fmt.Sprintf("/api/v1/user/starred/%s", repo)). + AddTokenAuth(tokenWithUserScope) + MakeRequest(t, req, http.StatusForbidden) + + user34 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 34}) + req = NewRequest(t, "PUT", fmt.Sprintf("/api/v1/user/starred/%s", repo)). + AddTokenAuth(getUserToken(t, user34.Name, auth_model.AccessTokenScopeWriteRepository)) + MakeRequest(t, req, http.StatusForbidden) + }) + + t.Run("GetStarredRepos", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/users/%s/starred", user)). + AddTokenAuth(token) + MakeRequest(t, req, http.StatusForbidden) + }) + + t.Run("GetMyStarredRepos", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + req := NewRequest(t, "GET", "/api/v1/user/starred"). + AddTokenAuth(tokenWithUserScope) + MakeRequest(t, req, http.StatusForbidden) + }) + + t.Run("IsStarring", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/starred/%s", repo)). + AddTokenAuth(tokenWithUserScope) + MakeRequest(t, req, http.StatusForbidden) + + req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/starred/%s", repo+"notexisting")). + AddTokenAuth(tokenWithUserScope) + MakeRequest(t, req, http.StatusForbidden) + }) + + t.Run("Unstar", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/user/starred/%s", repo)). + AddTokenAuth(tokenWithUserScope) + MakeRequest(t, req, http.StatusForbidden) + }) +}