mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-11-03 20:36:07 +01:00 
			
		
		
		
	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 <wxiaoguang@gmail.com>
This commit is contained in:
		@@ -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)
 | 
			
		||||
 
 | 
			
		||||
@@ -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 {
 | 
			
		||||
 
 | 
			
		||||
@@ -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 {
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										15
									
								
								templates/swagger/v1_json.tmpl
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										15
									
								
								templates/swagger/v1_json.tmpl
									
									
									
										generated
									
									
									
								
							@@ -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"
 | 
			
		||||
          }
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user