aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorXinyu Zhou <i@sourcehut.net>2022-12-28 05:21:14 +0800
committerGitHub <noreply@github.com>2022-12-27 15:21:14 -0600
commit7cc7db73b922143a1288d26b39eee9e8e59f7106 (patch)
treeda812b01e5953781a820f05edee52c93efef4bec
parent22a6e9759739af05a862fb5098aec0659aed2e84 (diff)
downloadgitea-7cc7db73b922143a1288d26b39eee9e8e59f7106.tar.gz
gitea-7cc7db73b922143a1288d26b39eee9e8e59f7106.zip
Add option to prohibit fork if user reached maximum limit of repositories (#21848)
If user has reached the maximum limit of repositories: - Before - disallow create - allow fork without limit - This patch: - disallow create - disallow fork - Add option `ALLOW_FORK_WITHOUT_MAXIMUM_LIMIT` (Default **true**) : enable this allow user fork repositories without maximum number limit fixed https://github.com/go-gitea/gitea/issues/21847 Signed-off-by: Xinyu Zhou <i@sourcehut.net>
-rw-r--r--custom/conf/app.example.ini3
-rw-r--r--docs/content/doc/advanced/config-cheat-sheet.en-us.md1
-rw-r--r--models/user/user.go9
-rw-r--r--modules/setting/repository.go2
-rw-r--r--routers/api/v1/repo/fork.go2
-rw-r--r--routers/web/repo/pull.go13
-rw-r--r--services/repository/fork.go7
-rw-r--r--services/repository/fork_test.go16
-rw-r--r--templates/repo/pulls/fork.tmpl2
9 files changed, 53 insertions, 2 deletions
diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini
index 754eab452f..cec5e8cf03 100644
--- a/custom/conf/app.example.ini
+++ b/custom/conf/app.example.ini
@@ -957,6 +957,9 @@ ROUTER = console
;; Don't allow download source archive files from UI
;DISABLE_DOWNLOAD_SOURCE_ARCHIVES = false
+;; Allow fork repositories without maximum number limit
+;ALLOW_FORK_WITHOUT_MAXIMUM_LIMIT = true
+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;[repository.editor]
diff --git a/docs/content/doc/advanced/config-cheat-sheet.en-us.md b/docs/content/doc/advanced/config-cheat-sheet.en-us.md
index 9d7a375ad3..3ccef3130c 100644
--- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md
+++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md
@@ -112,6 +112,7 @@ In addition there is _`StaticRootPath`_ which can be set as a built-in at build
- `ALLOW_ADOPTION_OF_UNADOPTED_REPOSITORIES`: **false**: Allow non-admin users to adopt unadopted repositories
- `ALLOW_DELETION_OF_UNADOPTED_REPOSITORIES`: **false**: Allow non-admin users to delete unadopted repositories
- `DISABLE_DOWNLOAD_SOURCE_ARCHIVES`: **false**: Don't allow download source archive files from UI
+- `ALLOW_FORK_WITHOUT_MAXIMUM_LIMIT`: **true**: Allow fork repositories without maximum number limit
### Repository - Editor (`repository.editor`)
diff --git a/models/user/user.go b/models/user/user.go
index 71b036b06f..825223201b 100644
--- a/models/user/user.go
+++ b/models/user/user.go
@@ -275,6 +275,15 @@ func (u *User) CanEditGitHook() bool {
return !setting.DisableGitHooks && (u.IsAdmin || u.AllowGitHook)
}
+// CanForkRepo returns if user login can fork a repository
+// It checks especially that the user can create repos, and potentially more
+func (u *User) CanForkRepo() bool {
+ if setting.Repository.AllowForkWithoutMaximumLimit {
+ return true
+ }
+ return u.CanCreateRepo()
+}
+
// CanImportLocal returns true if user can migrate repository by local path.
func (u *User) CanImportLocal() bool {
if !setting.ImportLocalPaths || u == nil {
diff --git a/modules/setting/repository.go b/modules/setting/repository.go
index ea288d2ed2..d78b63a1f3 100644
--- a/modules/setting/repository.go
+++ b/modules/setting/repository.go
@@ -48,6 +48,7 @@ var (
AllowAdoptionOfUnadoptedRepositories bool
AllowDeleteOfUnadoptedRepositories bool
DisableDownloadSourceArchives bool
+ AllowForkWithoutMaximumLimit bool
// Repository editor settings
Editor struct {
@@ -160,6 +161,7 @@ var (
DisableMigrations: false,
DisableStars: false,
DefaultBranch: "main",
+ AllowForkWithoutMaximumLimit: true,
// Repository editor settings
Editor: struct {
diff --git a/routers/api/v1/repo/fork.go b/routers/api/v1/repo/fork.go
index f2cd10e711..97c1dc7ba7 100644
--- a/routers/api/v1/repo/fork.go
+++ b/routers/api/v1/repo/fork.go
@@ -141,7 +141,7 @@ func CreateFork(ctx *context.APIContext) {
Description: repo.Description,
})
if err != nil {
- if repo_model.IsErrRepoAlreadyExist(err) {
+ if repo_model.IsErrReachLimitOfRepo(err) || repo_model.IsErrRepoAlreadyExist(err) {
ctx.Error(http.StatusConflict, "ForkRepository", err)
} else {
ctx.Error(http.StatusInternalServerError, "ForkRepository", err)
diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go
index bea6bfe433..8929a183ee 100644
--- a/routers/web/repo/pull.go
+++ b/routers/web/repo/pull.go
@@ -182,6 +182,15 @@ func getForkRepository(ctx *context.Context) *repo_model.Repository {
func Fork(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("new_fork")
+ if ctx.Doer.CanForkRepo() {
+ ctx.Data["CanForkRepo"] = true
+ } else {
+ maxCreationLimit := ctx.Doer.MaxCreationLimit()
+ msg := ctx.TrN(maxCreationLimit, "repo.form.reach_limit_of_creation_1", "repo.form.reach_limit_of_creation_n", maxCreationLimit)
+ ctx.Data["Flash"] = ctx.Flash
+ ctx.Flash.Error(msg)
+ }
+
getForkRepository(ctx)
if ctx.Written() {
return
@@ -254,6 +263,10 @@ func ForkPost(ctx *context.Context) {
if err != nil {
ctx.Data["Err_RepoName"] = true
switch {
+ case repo_model.IsErrReachLimitOfRepo(err):
+ maxCreationLimit := ctxUser.MaxCreationLimit()
+ msg := ctx.TrN(maxCreationLimit, "repo.form.reach_limit_of_creation_1", "repo.form.reach_limit_of_creation_n", maxCreationLimit)
+ ctx.RenderWithErr(msg, tplFork, &form)
case repo_model.IsErrRepoAlreadyExist(err):
ctx.RenderWithErr(ctx.Tr("repo.settings.new_owner_has_same_repo"), tplFork, &form)
case db.IsErrNameReserved(err):
diff --git a/services/repository/fork.go b/services/repository/fork.go
index 3ed0f4ffa5..ad534be887 100644
--- a/services/repository/fork.go
+++ b/services/repository/fork.go
@@ -51,6 +51,13 @@ type ForkRepoOptions struct {
// ForkRepository forks a repository
func ForkRepository(ctx context.Context, doer, owner *user_model.User, opts ForkRepoOptions) (*repo_model.Repository, error) {
+ // Fork is prohibited, if user has reached maximum limit of repositories
+ if !owner.CanForkRepo() {
+ return nil, repo_model.ErrReachLimitOfRepo{
+ Limit: owner.MaxRepoCreation,
+ }
+ }
+
forkedRepo, err := repo_model.GetUserFork(ctx, opts.BaseRepo.ID, owner.ID)
if err != nil {
return nil, err
diff --git a/services/repository/fork_test.go b/services/repository/fork_test.go
index c809ed4529..452798b25b 100644
--- a/services/repository/fork_test.go
+++ b/services/repository/fork_test.go
@@ -10,6 +10,7 @@ import (
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/setting"
"github.com/stretchr/testify/assert"
)
@@ -29,4 +30,19 @@ func TestForkRepository(t *testing.T) {
assert.Nil(t, fork)
assert.Error(t, err)
assert.True(t, IsErrForkAlreadyExist(err))
+
+ // user not reached maximum limit of repositories
+ assert.False(t, repo_model.IsErrReachLimitOfRepo(err))
+
+ // change AllowForkWithoutMaximumLimit to false for the test
+ setting.Repository.AllowForkWithoutMaximumLimit = false
+ // user has reached maximum limit of repositories
+ user.MaxRepoCreation = 0
+ fork2, err := ForkRepository(git.DefaultContext, user, user, ForkRepoOptions{
+ BaseRepo: repo,
+ Name: "test",
+ Description: "test",
+ })
+ assert.Nil(t, fork2)
+ assert.True(t, repo_model.IsErrReachLimitOfRepo(err))
}
diff --git a/templates/repo/pulls/fork.tmpl b/templates/repo/pulls/fork.tmpl
index 28ba8e2e25..4e20642cf6 100644
--- a/templates/repo/pulls/fork.tmpl
+++ b/templates/repo/pulls/fork.tmpl
@@ -58,7 +58,7 @@
<div class="inline field">
<label></label>
- <button class="ui green button">
+ <button class="ui green button{{if not .CanForkRepo}} disabled{{end}}">
{{.locale.Tr "repo.fork_repo"}}
</button>
</div>