feat(repo): split repository creation limit into user and org scopes (#37872)

## Background

`MAX_CREATION_LIMIT` applies to whoever owns a new repository, with no
distinction between individual users and organizations. Admins who want
different limits for the two - most commonly "block personal repos but
let orgs create freely" - currently have to set per-user / per-org
overrides on every entity.

## Changes

Adds two new `[repository]` settings:

- `USER_MAX_CREATION_LIMIT`: global limit for individual users
- `ORG_MAX_CREATION_LIMIT`: global limit for organizations

`MAX_CREATION_LIMIT` is kept as a shortcut: when set, it becomes the
default value for both new keys. When the new keys are explicitly
configured, they take precedence. Deployments that only set
`MAX_CREATION_LIMIT` see behavior identical to now.

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
This commit is contained in:
Zettat123
2026-05-28 11:29:32 -06:00
committed by GitHub
parent 52fef74291
commit 49f88a4b9e
7 changed files with 137 additions and 18 deletions
+2 -5
View File
@@ -20,7 +20,6 @@ import (
"gitea.dev/modules/gitrepo"
"gitea.dev/modules/globallock"
"gitea.dev/modules/log"
"gitea.dev/modules/setting"
"gitea.dev/modules/util"
notify_service "gitea.dev/services/notify"
)
@@ -62,8 +61,7 @@ func AcceptTransferOwnership(ctx context.Context, repo *repo_model.Repository, d
}
if !doer.CanCreateRepoIn(repoTransfer.Recipient) {
limit := util.Iif(repoTransfer.Recipient.MaxRepoCreation >= 0, repoTransfer.Recipient.MaxRepoCreation, setting.Repository.MaxCreationLimit)
return LimitReachedError{Limit: limit}
return LimitReachedError{Limit: repoTransfer.Recipient.MaxCreationLimit()}
}
if !repoTransfer.CanUserAcceptOrRejectTransfer(ctx, doer) {
@@ -434,8 +432,7 @@ func StartRepositoryTransfer(ctx context.Context, doer, newOwner *user_model.Use
}
if !doer.CanForkRepoIn(newOwner) {
limit := util.Iif(newOwner.MaxRepoCreation >= 0, newOwner.MaxRepoCreation, setting.Repository.MaxCreationLimit)
return LimitReachedError{Limit: limit}
return LimitReachedError{Limit: newOwner.MaxCreationLimit()}
}
var isDirectTransfer bool
+2
View File
@@ -133,6 +133,8 @@ func TestRepositoryTransferRejection(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
// Set limit to 0 repositories so no repositories can be transferred
defer test.MockVariableValue(&setting.Repository.MaxCreationLimit, 0)()
defer test.MockVariableValue(&setting.Repository.UserMaxCreationLimit, 0)()
defer test.MockVariableValue(&setting.Repository.OrgMaxCreationLimit, 0)()
// Admin case
doerAdmin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})