From c49823c2b6dd4dca46f56e1c4b380d041c6edeb4 Mon Sep 17 00:00:00 2001 From: HeCorr <75134774+HeCorr@users.noreply.github.com> Date: Fri, 10 Jan 2025 20:01:33 -0300 Subject: [PATCH 1/9] api: respect DISABLE_STARS config option - add APIGone response type - re-generate Swagger spec if stars are disabled: - render 404 page on /repo/stars - return 410 error on star-related API endpoints and actions - return empty value on star model functions (StarRepo, IsStaring, GetStargazers) --- models/repo/star.go | 16 +++++++++++++++ routers/api/v1/repo/star.go | 8 ++++++++ routers/api/v1/user/star.go | 36 ++++++++++++++++++++++++++++++++++ routers/web/repo/view.go | 4 ++++ services/context/api.go | 6 ++++++ templates/swagger/v1_json.tmpl | 29 +++++++++++++++++++++++++++ 6 files changed, 99 insertions(+) diff --git a/models/repo/star.go b/models/repo/star.go index 4c66855525fa6..6f649a95dacb4 100644 --- a/models/repo/star.go +++ b/models/repo/star.go @@ -8,6 +8,7 @@ import ( "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" ) @@ -24,7 +25,12 @@ func init() { } // StarRepo or unstar repository. +// +// Will do nothing if stars are disabled. func StarRepo(ctx context.Context, doer *user_model.User, repo *Repository, star bool) error { + if setting.Repository.DisableStars { + return nil + } ctx, committer, err := db.TxContext(ctx) if err != nil { return err @@ -70,13 +76,23 @@ func StarRepo(ctx context.Context, doer *user_model.User, repo *Repository, star } // IsStaring checks if user has starred given repository. +// +// Will always return false if stars are disabled. func IsStaring(ctx context.Context, userID, repoID int64) bool { + if setting.Repository.DisableStars { + return false + } has, _ := db.GetEngine(ctx).Get(&Star{UID: userID, RepoID: repoID}) return has } // GetStargazers returns the users that starred the repo. +// +// Will always return an empty slice if stars are disabled. func GetStargazers(ctx context.Context, repo *Repository, opts db.ListOptions) ([]*user_model.User, error) { + if setting.Repository.DisableStars { + return make([]*user_model.User, 0), nil + } sess := db.GetEngine(ctx).Where("star.repo_id = ?", repo.ID). Join("LEFT", "star", "`user`.id = star.uid") if opts.Page > 0 { diff --git a/routers/api/v1/repo/star.go b/routers/api/v1/repo/star.go index 99676de119c1f..89cfefcad3069 100644 --- a/routers/api/v1/repo/star.go +++ b/routers/api/v1/repo/star.go @@ -7,6 +7,7 @@ import ( "net/http" repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/routers/api/v1/utils" "code.gitea.io/gitea/services/context" @@ -44,6 +45,13 @@ func ListStargazers(ctx *context.APIContext) { // "$ref": "#/responses/UserList" // "404": // "$ref": "#/responses/notFound" + // "410": + // "$ref": "#/responses/gone" + + if setting.Repository.DisableStars { + ctx.Error(http.StatusGone, "StarsDisabled", "Stars are disabled.") + return + } 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..848c109fa5f1b 100644 --- a/routers/api/v1/user/star.go +++ b/routers/api/v1/user/star.go @@ -11,6 +11,7 @@ import ( access_model "code.gitea.io/gitea/models/perm/access" repo_model "code.gitea.io/gitea/models/repo" 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/routers/api/v1/utils" "code.gitea.io/gitea/services/context" @@ -66,6 +67,13 @@ func GetStarredRepos(ctx *context.APIContext) { // "$ref": "#/responses/RepositoryList" // "404": // "$ref": "#/responses/notFound" + // "410": + // "$ref": "#/responses/gone" + + if setting.Repository.DisableStars { + ctx.Error(http.StatusGone, "StarsDisabled", "Stars are disabled.") + return + } private := ctx.ContextUser.ID == ctx.Doer.ID repos, err := getStarredRepos(ctx, ctx.ContextUser, private) @@ -97,6 +105,13 @@ func GetMyStarredRepos(ctx *context.APIContext) { // responses: // "200": // "$ref": "#/responses/RepositoryList" + // "410": + // "$ref": "#/responses/gone" + + if setting.Repository.DisableStars { + ctx.Error(http.StatusGone, "StarsDisabled", "Stars are disabled.") + return + } repos, err := getStarredRepos(ctx, ctx.Doer, true) if err != nil { @@ -128,6 +143,13 @@ func IsStarring(ctx *context.APIContext) { // "$ref": "#/responses/empty" // "404": // "$ref": "#/responses/notFound" + // "410": + // "$ref": "#/responses/gone" + + if setting.Repository.DisableStars { + ctx.Error(http.StatusGone, "StarsDisabled", "Stars are disabled.") + return + } if repo_model.IsStaring(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID) { ctx.Status(http.StatusNoContent) @@ -159,6 +181,13 @@ func Star(ctx *context.APIContext) { // "$ref": "#/responses/forbidden" // "404": // "$ref": "#/responses/notFound" + // "410": + // "$ref": "#/responses/gone" + + if setting.Repository.DisableStars { + ctx.Error(http.StatusGone, "StarsDisabled", "Stars are disabled.") + return + } err := repo_model.StarRepo(ctx, ctx.Doer, ctx.Repo.Repository, true) if err != nil { @@ -193,6 +222,13 @@ func Unstar(ctx *context.APIContext) { // "$ref": "#/responses/empty" // "404": // "$ref": "#/responses/notFound" + // "410": + // "$ref": "#/responses/gone" + + if setting.Repository.DisableStars { + ctx.Error(http.StatusGone, "StarsDisabled", "Stars are disabled.") + return + } err := repo_model.StarRepo(ctx, ctx.Doer, ctx.Repo.Repository, false) if err != nil { diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index 9fe2b58ebc66d..aec16565a04ad 100644 --- a/routers/web/repo/view.go +++ b/routers/web/repo/view.go @@ -346,6 +346,10 @@ func Watchers(ctx *context.Context) { // Stars render repository's starred users func Stars(ctx *context.Context) { + if setting.Repository.DisableStars { + ctx.NotFound("Stars disabled", nil) + return + } ctx.Data["Title"] = ctx.Tr("repo.stargazers") ctx.Data["CardsTitle"] = ctx.Tr("repo.stargazers") RenderUserCards(ctx, ctx.Repo.Repository.NumStars, func(opts db.ListOptions) ([]*user_model.User, error) { diff --git a/services/context/api.go b/services/context/api.go index 3a3cbe670e4fa..b1612903c6750 100644 --- a/services/context/api.go +++ b/services/context/api.go @@ -89,6 +89,12 @@ type APINotFound struct{} // swagger:response conflict type APIConflict struct{} +// APIGone means the functionality has been disabled +// swagger:response gone +type APIGone struct { + APIError +} + // APIRedirect is a redirect response // swagger:response redirect type APIRedirect struct{} diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index fb37d45ce8de2..1ea90df1edd18 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -13758,6 +13758,9 @@ }, "404": { "$ref": "#/responses/notFound" + }, + "410": { + "$ref": "#/responses/gone" } } } @@ -17494,6 +17497,9 @@ "responses": { "200": { "$ref": "#/responses/RepositoryList" + }, + "410": { + "$ref": "#/responses/gone" } } } @@ -17527,6 +17533,9 @@ }, "404": { "$ref": "#/responses/notFound" + }, + "410": { + "$ref": "#/responses/gone" } } }, @@ -17561,6 +17570,9 @@ }, "404": { "$ref": "#/responses/notFound" + }, + "410": { + "$ref": "#/responses/gone" } } }, @@ -17592,6 +17604,9 @@ }, "404": { "$ref": "#/responses/notFound" + }, + "410": { + "$ref": "#/responses/gone" } } } @@ -18268,6 +18283,9 @@ }, "404": { "$ref": "#/responses/notFound" + }, + "410": { + "$ref": "#/responses/gone" } } } @@ -26599,6 +26617,17 @@ } } }, + "gone": { + "description": "APIGone means the functionality has been disabled", + "headers": { + "message": { + "type": "string" + }, + "url": { + "type": "string" + } + } + }, "invalidTopicsError": { "description": "APIInvalidTopicsError is error format response to invalid topics", "headers": { From 2c5d55397e801fdbd022451b2c33bdd6e89114f9 Mon Sep 17 00:00:00 2001 From: HeCorr <75134774+HeCorr@users.noreply.github.com> Date: Thu, 23 Jan 2025 10:27:32 -0300 Subject: [PATCH 2/9] api: remove disabled stars check from models also return 403 Forbidden instead of 410 Gone --- models/repo/star.go | 16 ---------------- routers/api/v1/repo/star.go | 6 +++--- routers/api/v1/user/star.go | 28 +++++++++++++--------------- routers/web/repo/repo.go | 21 +++++++++++++++------ 4 files changed, 31 insertions(+), 40 deletions(-) diff --git a/models/repo/star.go b/models/repo/star.go index 6f649a95dacb4..4c66855525fa6 100644 --- a/models/repo/star.go +++ b/models/repo/star.go @@ -8,7 +8,6 @@ import ( "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" ) @@ -25,12 +24,7 @@ func init() { } // StarRepo or unstar repository. -// -// Will do nothing if stars are disabled. func StarRepo(ctx context.Context, doer *user_model.User, repo *Repository, star bool) error { - if setting.Repository.DisableStars { - return nil - } ctx, committer, err := db.TxContext(ctx) if err != nil { return err @@ -76,23 +70,13 @@ func StarRepo(ctx context.Context, doer *user_model.User, repo *Repository, star } // IsStaring checks if user has starred given repository. -// -// Will always return false if stars are disabled. func IsStaring(ctx context.Context, userID, repoID int64) bool { - if setting.Repository.DisableStars { - return false - } has, _ := db.GetEngine(ctx).Get(&Star{UID: userID, RepoID: repoID}) return has } // GetStargazers returns the users that starred the repo. -// -// Will always return an empty slice if stars are disabled. func GetStargazers(ctx context.Context, repo *Repository, opts db.ListOptions) ([]*user_model.User, error) { - if setting.Repository.DisableStars { - return make([]*user_model.User, 0), nil - } sess := db.GetEngine(ctx).Where("star.repo_id = ?", repo.ID). Join("LEFT", "star", "`user`.id = star.uid") if opts.Page > 0 { diff --git a/routers/api/v1/repo/star.go b/routers/api/v1/repo/star.go index 89cfefcad3069..ecb1bfa0f7459 100644 --- a/routers/api/v1/repo/star.go +++ b/routers/api/v1/repo/star.go @@ -45,11 +45,11 @@ func ListStargazers(ctx *context.APIContext) { // "$ref": "#/responses/UserList" // "404": // "$ref": "#/responses/notFound" - // "410": - // "$ref": "#/responses/gone" + // "403": + // "$ref": "#/responses/forbidden" if setting.Repository.DisableStars { - ctx.Error(http.StatusGone, "StarsDisabled", "Stars are disabled.") + ctx.Error(http.StatusForbidden, "StarsDisabled", "Stars are disabled.") return } diff --git a/routers/api/v1/user/star.go b/routers/api/v1/user/star.go index 848c109fa5f1b..db4ab1f3c7d26 100644 --- a/routers/api/v1/user/star.go +++ b/routers/api/v1/user/star.go @@ -67,11 +67,11 @@ func GetStarredRepos(ctx *context.APIContext) { // "$ref": "#/responses/RepositoryList" // "404": // "$ref": "#/responses/notFound" - // "410": - // "$ref": "#/responses/gone" + // "403": + // "$ref": "#/responses/forbidden" if setting.Repository.DisableStars { - ctx.Error(http.StatusGone, "StarsDisabled", "Stars are disabled.") + ctx.Error(http.StatusForbidden, "StarsDisabled", "Stars are disabled.") return } @@ -105,11 +105,11 @@ func GetMyStarredRepos(ctx *context.APIContext) { // responses: // "200": // "$ref": "#/responses/RepositoryList" - // "410": - // "$ref": "#/responses/gone" + // "403": + // "$ref": "#/responses/forbidden" if setting.Repository.DisableStars { - ctx.Error(http.StatusGone, "StarsDisabled", "Stars are disabled.") + ctx.Error(http.StatusForbidden, "StarsDisabled", "Stars are disabled.") return } @@ -143,11 +143,11 @@ func IsStarring(ctx *context.APIContext) { // "$ref": "#/responses/empty" // "404": // "$ref": "#/responses/notFound" - // "410": - // "$ref": "#/responses/gone" + // "403": + // "$ref": "#/responses/forbidden" if setting.Repository.DisableStars { - ctx.Error(http.StatusGone, "StarsDisabled", "Stars are disabled.") + ctx.Error(http.StatusForbidden, "StarsDisabled", "Stars are disabled.") return } @@ -181,11 +181,9 @@ func Star(ctx *context.APIContext) { // "$ref": "#/responses/forbidden" // "404": // "$ref": "#/responses/notFound" - // "410": - // "$ref": "#/responses/gone" if setting.Repository.DisableStars { - ctx.Error(http.StatusGone, "StarsDisabled", "Stars are disabled.") + ctx.Error(http.StatusForbidden, "StarsDisabled", "Stars are disabled.") return } @@ -222,11 +220,11 @@ func Unstar(ctx *context.APIContext) { // "$ref": "#/responses/empty" // "404": // "$ref": "#/responses/notFound" - // "410": - // "$ref": "#/responses/gone" + // "403": + // "$ref": "#/responses/forbidden" if setting.Repository.DisableStars { - ctx.Error(http.StatusGone, "StarsDisabled", "Stars are disabled.") + ctx.Error(http.StatusForbidden, "StarsDisabled", "Stars are disabled.") return } diff --git a/routers/web/repo/repo.go b/routers/web/repo/repo.go index 0f408b22e096d..6f3d93785aff9 100644 --- a/routers/web/repo/repo.go +++ b/routers/web/repo/repo.go @@ -317,10 +317,13 @@ func Action(ctx *context.Context) { err = repo_model.WatchRepo(ctx, ctx.Doer, ctx.Repo.Repository, true) case "unwatch": err = repo_model.WatchRepo(ctx, ctx.Doer, ctx.Repo.Repository, false) - case "star": - err = repo_model.StarRepo(ctx, ctx.Doer, ctx.Repo.Repository, true) - case "unstar": - err = repo_model.StarRepo(ctx, ctx.Doer, ctx.Repo.Repository, false) + case "star", "unstar": + if setting.Repository.DisableStars { + err = errors.New("stars are disabled") + } else { + err = repo_model.StarRepo(ctx, ctx.Doer, + ctx.Repo.Repository, ctx.PathParam("action") == "star") + } case "accept_transfer": err = acceptOrRejectRepoTransfer(ctx, true) case "reject_transfer": @@ -349,7 +352,11 @@ func Action(ctx *context.Context) { case "watch", "unwatch": ctx.Data["IsWatchingRepo"] = repo_model.IsWatching(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID) case "star", "unstar": - ctx.Data["IsStaringRepo"] = repo_model.IsStaring(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID) + if setting.Repository.DisableStars { + ctx.Data["IsStaringRepo"] = false + } else { + ctx.Data["IsStaringRepo"] = repo_model.IsStaring(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID) + } } // see the `hx-trigger="refreshUserCards ..."` comments in tmpl @@ -370,7 +377,9 @@ func Action(ctx *context.Context) { ctx.HTML(http.StatusOK, tplWatchUnwatch) return case "star", "unstar": - ctx.HTML(http.StatusOK, tplStarUnstar) + if !setting.Repository.DisableStars { + ctx.HTML(http.StatusOK, tplStarUnstar) + } return } From 10d9507ec16d2328c1ba814e69bcbd6855dc986d Mon Sep 17 00:00:00 2001 From: HeCorr <75134774+HeCorr@users.noreply.github.com> Date: Thu, 23 Jan 2025 10:43:16 -0300 Subject: [PATCH 3/9] api: remove APIGone and regen swagger template also undo disabled stars check before sending StarUnstar template in Action() as the action already checks for it. --- routers/web/repo/repo.go | 4 +--- services/context/api.go | 6 ----- templates/swagger/v1_json.tmpl | 42 ++++++++++++---------------------- 3 files changed, 15 insertions(+), 37 deletions(-) diff --git a/routers/web/repo/repo.go b/routers/web/repo/repo.go index aa265db0af6c0..ac922bb2bdb48 100644 --- a/routers/web/repo/repo.go +++ b/routers/web/repo/repo.go @@ -377,9 +377,7 @@ func Action(ctx *context.Context) { ctx.HTML(http.StatusOK, tplWatchUnwatch) return case "star", "unstar": - if !setting.Repository.DisableStars { - ctx.HTML(http.StatusOK, tplStarUnstar) - } + ctx.HTML(http.StatusOK, tplStarUnstar) return } diff --git a/services/context/api.go b/services/context/api.go index b1612903c6750..3a3cbe670e4fa 100644 --- a/services/context/api.go +++ b/services/context/api.go @@ -89,12 +89,6 @@ type APINotFound struct{} // swagger:response conflict type APIConflict struct{} -// APIGone means the functionality has been disabled -// swagger:response gone -type APIGone struct { - APIError -} - // APIRedirect is a redirect response // swagger:response redirect type APIRedirect struct{} diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 283fd834c1f0e..753686ea994ee 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -13768,11 +13768,11 @@ "200": { "$ref": "#/responses/UserList" }, + "403": { + "$ref": "#/responses/forbidden" + }, "404": { "$ref": "#/responses/notFound" - }, - "410": { - "$ref": "#/responses/gone" } } } @@ -17510,8 +17510,8 @@ "200": { "$ref": "#/responses/RepositoryList" }, - "410": { - "$ref": "#/responses/gone" + "403": { + "$ref": "#/responses/forbidden" } } } @@ -17543,11 +17543,11 @@ "204": { "$ref": "#/responses/empty" }, + "403": { + "$ref": "#/responses/forbidden" + }, "404": { "$ref": "#/responses/notFound" - }, - "410": { - "$ref": "#/responses/gone" } } }, @@ -17582,9 +17582,6 @@ }, "404": { "$ref": "#/responses/notFound" - }, - "410": { - "$ref": "#/responses/gone" } } }, @@ -17614,11 +17611,11 @@ "204": { "$ref": "#/responses/empty" }, + "403": { + "$ref": "#/responses/forbidden" + }, "404": { "$ref": "#/responses/notFound" - }, - "410": { - "$ref": "#/responses/gone" } } } @@ -18293,11 +18290,11 @@ "200": { "$ref": "#/responses/RepositoryList" }, + "403": { + "$ref": "#/responses/forbidden" + }, "404": { "$ref": "#/responses/notFound" - }, - "410": { - "$ref": "#/responses/gone" } } } @@ -26629,17 +26626,6 @@ } } }, - "gone": { - "description": "APIGone means the functionality has been disabled", - "headers": { - "message": { - "type": "string" - }, - "url": { - "type": "string" - } - } - }, "invalidTopicsError": { "description": "APIInvalidTopicsError is error format response to invalid topics", "headers": { From 1b9d569812c05282f7c8dfaac0cee7fc5b105644 Mon Sep 17 00:00:00 2001 From: HeCorr <75134774+HeCorr@users.noreply.github.com> Date: Mon, 3 Feb 2025 02:44:45 +0000 Subject: [PATCH 4/9] Check for DisableStars inside middlewares --- routers/api/v1/api.go | 16 +++++++++++++--- routers/api/v1/repo/star.go | 6 ------ routers/api/v1/user/star.go | 26 -------------------------- routers/web/repo/view.go | 4 ---- routers/web/web.go | 9 ++++++++- 5 files changed, 21 insertions(+), 40 deletions(-) diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 438db4ae71b90..48923972d1683 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", user.GetStarredRepos, reqStarsEnabled()) 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", repo.ListStargazers, reqStarsEnabled()) 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 ecb1bfa0f7459..46ed17ad91eb6 100644 --- a/routers/api/v1/repo/star.go +++ b/routers/api/v1/repo/star.go @@ -7,7 +7,6 @@ import ( "net/http" repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/routers/api/v1/utils" "code.gitea.io/gitea/services/context" @@ -48,11 +47,6 @@ func ListStargazers(ctx *context.APIContext) { // "403": // "$ref": "#/responses/forbidden" - if setting.Repository.DisableStars { - ctx.Error(http.StatusForbidden, "StarsDisabled", "Stars are disabled.") - return - } - stargazers, err := repo_model.GetStargazers(ctx, ctx.Repo.Repository, utils.GetListOptions(ctx)) if err != nil { ctx.Error(http.StatusInternalServerError, "GetStargazers", err) diff --git a/routers/api/v1/user/star.go b/routers/api/v1/user/star.go index db4ab1f3c7d26..70e54bc1ae4d9 100644 --- a/routers/api/v1/user/star.go +++ b/routers/api/v1/user/star.go @@ -11,7 +11,6 @@ import ( access_model "code.gitea.io/gitea/models/perm/access" repo_model "code.gitea.io/gitea/models/repo" 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/routers/api/v1/utils" "code.gitea.io/gitea/services/context" @@ -70,11 +69,6 @@ func GetStarredRepos(ctx *context.APIContext) { // "403": // "$ref": "#/responses/forbidden" - if setting.Repository.DisableStars { - ctx.Error(http.StatusForbidden, "StarsDisabled", "Stars are disabled.") - return - } - private := ctx.ContextUser.ID == ctx.Doer.ID repos, err := getStarredRepos(ctx, ctx.ContextUser, private) if err != nil { @@ -108,11 +102,6 @@ func GetMyStarredRepos(ctx *context.APIContext) { // "403": // "$ref": "#/responses/forbidden" - if setting.Repository.DisableStars { - ctx.Error(http.StatusForbidden, "StarsDisabled", "Stars are disabled.") - return - } - repos, err := getStarredRepos(ctx, ctx.Doer, true) if err != nil { ctx.Error(http.StatusInternalServerError, "getStarredRepos", err) @@ -146,11 +135,6 @@ func IsStarring(ctx *context.APIContext) { // "403": // "$ref": "#/responses/forbidden" - if setting.Repository.DisableStars { - ctx.Error(http.StatusForbidden, "StarsDisabled", "Stars are disabled.") - return - } - if repo_model.IsStaring(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID) { ctx.Status(http.StatusNoContent) } else { @@ -182,11 +166,6 @@ func Star(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" - if setting.Repository.DisableStars { - ctx.Error(http.StatusForbidden, "StarsDisabled", "Stars are disabled.") - return - } - err := repo_model.StarRepo(ctx, ctx.Doer, ctx.Repo.Repository, true) if err != nil { if errors.Is(err, user_model.ErrBlockedUser) { @@ -223,11 +202,6 @@ func Unstar(ctx *context.APIContext) { // "403": // "$ref": "#/responses/forbidden" - if setting.Repository.DisableStars { - ctx.Error(http.StatusForbidden, "StarsDisabled", "Stars are disabled.") - return - } - err := repo_model.StarRepo(ctx, ctx.Doer, ctx.Repo.Repository, false) if err != nil { ctx.Error(http.StatusInternalServerError, "StarRepo", err) diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index d1360f41ca96c..05ecf2ab794b3 100644 --- a/routers/web/repo/view.go +++ b/routers/web/repo/view.go @@ -346,10 +346,6 @@ func Watchers(ctx *context.Context) { // Stars render repository's starred users func Stars(ctx *context.Context) { - if setting.Repository.DisableStars { - ctx.NotFound("Stars disabled", nil) - return - } ctx.Data["Title"] = ctx.Tr("repo.stargazers") ctx.Data["CardsTitle"] = ctx.Tr("repo.stargazers") RenderUserCards(ctx, ctx.Repo.Repository.NumStars, func(opts db.ListOptions) ([]*user_model.User, error) { diff --git a/routers/web/web.go b/routers/web/web.go index f772f6dbb979d..56b1acf7c173b 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,7 +1600,7 @@ func registerRoutes(m *web.Router) { // end "/{username}/{reponame}": repo code m.Group("/{username}/{reponame}", func() { - m.Get("/stars", repo.Stars) + m.Get("/stars", repo.Stars, starsEnabled) m.Get("/watchers", repo.Watchers) m.Get("/search", reqUnitCodeReader, repo.Search) m.Post("/action/{action}", reqSignIn, repo.Action) From 999e43db19c5cb648cc5c19fcc65682837528d34 Mon Sep 17 00:00:00 2001 From: HeCorr <75134774+HeCorr@users.noreply.github.com> Date: Mon, 3 Feb 2025 03:41:17 +0000 Subject: [PATCH 5/9] Middlewares go before handlers in m.Get() --- routers/api/v1/api.go | 4 ++-- routers/web/web.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 48923972d1683..0aa38b8b6abbb 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -1005,7 +1005,7 @@ func Routes() *web.Router { m.Get("/{target}", user.CheckFollowing) }) - m.Get("/starred", user.GetStarredRepos, reqStarsEnabled()) + m.Get("/starred", reqStarsEnabled(), user.GetStarredRepos) m.Get("/subscriptions", user.GetWatchedRepos) }, context.UserAssignmentAPI(), checkTokenPublicOnly()) @@ -1258,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, reqStarsEnabled()) + m.Get("/stargazers", reqStarsEnabled(), repo.ListStargazers) m.Get("/subscribers", repo.ListSubscribers) m.Group("/subscription", func() { m.Get("", user.IsWatching) diff --git a/routers/web/web.go b/routers/web/web.go index 56b1acf7c173b..b81009a894800 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -1600,7 +1600,7 @@ func registerRoutes(m *web.Router) { // end "/{username}/{reponame}": repo code m.Group("/{username}/{reponame}", func() { - m.Get("/stars", repo.Stars, starsEnabled) + m.Get("/stars", starsEnabled, repo.Stars) m.Get("/watchers", repo.Watchers) m.Get("/search", reqUnitCodeReader, repo.Search) m.Post("/action/{action}", reqSignIn, repo.Action) From df4f803eb0b01ddca51fea5cec39c0c4036eb708 Mon Sep 17 00:00:00 2001 From: HeCorr <75134774+HeCorr@users.noreply.github.com> Date: Mon, 3 Feb 2025 03:55:37 +0000 Subject: [PATCH 6/9] Write APIStarDisabled tests --- tests/integration/api_user_star_test.go | 64 +++++++++++++++++++++++++ 1 file changed, 64 insertions(+) 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) + }) +} From 59b914775e2c3961e0fef200a7b8fad1b4bff4da Mon Sep 17 00:00:00 2001 From: HeCorr <75134774+HeCorr@users.noreply.github.com> Date: Mon, 3 Feb 2025 19:10:32 +0000 Subject: [PATCH 7/9] Use middleware for DisableStars check on actions too --- routers/web/repo/repo.go | 8 ++------ routers/web/web.go | 10 +++++++++- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/routers/web/repo/repo.go b/routers/web/repo/repo.go index a5cae7c2ed9bc..2c0c6742ca20d 100644 --- a/routers/web/repo/repo.go +++ b/routers/web/repo/repo.go @@ -348,12 +348,8 @@ func Action(ctx *context.Context) { case "unwatch": err = repo_model.WatchRepo(ctx, ctx.Doer, ctx.Repo.Repository, false) case "star", "unstar": - if setting.Repository.DisableStars { - err = errors.New("stars are disabled") - } else { - err = repo_model.StarRepo(ctx, ctx.Doer, - ctx.Repo.Repository, ctx.PathParam("action") == "star") - } + err = repo_model.StarRepo(ctx, ctx.Doer, + ctx.Repo.Repository, ctx.PathParam("action") == "star") case "accept_transfer": acceptTransfer(ctx) return diff --git a/routers/web/web.go b/routers/web/web.go index b81009a894800..9c48723a372b1 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -851,6 +851,12 @@ func registerRoutes(m *web.Router) { } } + reqStarsEnabled := func(ctx *context.Context) { + if setting.Repository.DisableStars { + ctx.Error(http.StatusForbidden, "stars are disabled") + } + } + individualPermsChecker := func(ctx *context.Context) { // org permissions have been checked in context.OrgAssignment(), but individual permissions haven't been checked. if ctx.ContextUser.IsIndividual() { @@ -1603,7 +1609,9 @@ func registerRoutes(m *web.Router) { 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, reqStarsEnabled, 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 From eb07becfc9d00ca03f8e4218d9fe5ffe7b75781f Mon Sep 17 00:00:00 2001 From: HeCorr <75134774+HeCorr@users.noreply.github.com> Date: Mon, 3 Feb 2025 19:17:09 +0000 Subject: [PATCH 8/9] Deduplicate stars middlewares oops. --- routers/web/repo/repo.go | 6 +----- routers/web/web.go | 8 +------- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/routers/web/repo/repo.go b/routers/web/repo/repo.go index 2c0c6742ca20d..55a9ae8e11b26 100644 --- a/routers/web/repo/repo.go +++ b/routers/web/repo/repo.go @@ -376,11 +376,7 @@ func Action(ctx *context.Context) { case "watch", "unwatch": ctx.Data["IsWatchingRepo"] = repo_model.IsWatching(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID) case "star", "unstar": - if setting.Repository.DisableStars { - ctx.Data["IsStaringRepo"] = false - } else { - ctx.Data["IsStaringRepo"] = repo_model.IsStaring(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID) - } + ctx.Data["IsStaringRepo"] = repo_model.IsStaring(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID) } // see the `hx-trigger="refreshUserCards ..."` comments in tmpl diff --git a/routers/web/web.go b/routers/web/web.go index 9c48723a372b1..3cb6dc2551f71 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -851,12 +851,6 @@ func registerRoutes(m *web.Router) { } } - reqStarsEnabled := func(ctx *context.Context) { - if setting.Repository.DisableStars { - ctx.Error(http.StatusForbidden, "stars are disabled") - } - } - individualPermsChecker := func(ctx *context.Context) { // org permissions have been checked in context.OrgAssignment(), but individual permissions haven't been checked. if ctx.ContextUser.IsIndividual() { @@ -1609,7 +1603,7 @@ func registerRoutes(m *web.Router) { m.Get("/stars", starsEnabled, repo.Stars) m.Get("/watchers", repo.Watchers) m.Get("/search", reqUnitCodeReader, repo.Search) - m.Post("/action/{action:star|unstar}", reqSignIn, reqStarsEnabled, 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) From 2458ac0efc20ddd94dabbbdbc47951338c58d08e Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Tue, 4 Feb 2025 09:27:37 +0800 Subject: [PATCH 9/9] Update routers/web/repo/repo.go --- routers/web/repo/repo.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/routers/web/repo/repo.go b/routers/web/repo/repo.go index 55a9ae8e11b26..8ebf5bcf3903b 100644 --- a/routers/web/repo/repo.go +++ b/routers/web/repo/repo.go @@ -347,9 +347,10 @@ func Action(ctx *context.Context) { err = repo_model.WatchRepo(ctx, ctx.Doer, ctx.Repo.Repository, true) case "unwatch": err = repo_model.WatchRepo(ctx, ctx.Doer, ctx.Repo.Repository, false) - case "star", "unstar": - err = repo_model.StarRepo(ctx, ctx.Doer, - ctx.Repo.Repository, ctx.PathParam("action") == "star") + case "star": + err = repo_model.StarRepo(ctx, ctx.Doer, ctx.Repo.Repository, true) + case "unstar": + err = repo_model.StarRepo(ctx, ctx.Doer, ctx.Repo.Repository, false) case "accept_transfer": acceptTransfer(ctx) return