Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions models/organization/org.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,12 +178,6 @@ func (org *Organization) HomeLink() string {
return org.AsUser().HomeLink()
}

// CanCreateRepo returns if user login can create a repository
// NOTE: functions calling this assume a failure due to repository count limit; if new checks are added, those functions should be revised
func (org *Organization) CanCreateRepo() bool {
return org.AsUser().CanCreateRepo()
}

// FindOrgMembersOpts represensts find org members conditions
type FindOrgMembersOpts struct {
db.ListOptions
Expand Down
16 changes: 8 additions & 8 deletions models/repo/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,31 +111,31 @@ func (err ErrRepoFilesAlreadyExist) Unwrap() error {
return util.ErrAlreadyExist
}

// CheckCreateRepository check if could created a repository
func CheckCreateRepository(ctx context.Context, doer, u *user_model.User, name string, overwriteOrAdopt bool) error {
if !doer.CanCreateRepo() {
return ErrReachLimitOfRepo{u.MaxRepoCreation}
// CheckCreateRepository check if doer could create a repository in new owner
func CheckCreateRepository(ctx context.Context, doer, owner *user_model.User, name string, overwriteOrAdopt bool) error {
if !doer.CanCreateRepoIn(owner) {
return ErrReachLimitOfRepo{owner.MaxRepoCreation}
}

if err := IsUsableRepoName(name); err != nil {
return err
}

has, err := IsRepositoryModelOrDirExist(ctx, u, name)
has, err := IsRepositoryModelOrDirExist(ctx, owner, name)
if err != nil {
return fmt.Errorf("IsRepositoryExist: %w", err)
} else if has {
return ErrRepoAlreadyExist{u.Name, name}
return ErrRepoAlreadyExist{owner.Name, name}
}

repoPath := RepoPath(u.Name, name)
repoPath := RepoPath(owner.Name, name)
isExist, err := util.IsExist(repoPath)
if err != nil {
log.Error("Unable to check if %s exists. Error: %v", repoPath, err)
return err
}
if !overwriteOrAdopt && isExist {
return ErrRepoFilesAlreadyExist{u.Name, name}
return ErrRepoFilesAlreadyExist{owner.Name, name}
}
return nil
}
Expand Down
20 changes: 9 additions & 11 deletions models/user/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,19 +247,19 @@ func (u *User) MaxCreationLimit() int {
return u.MaxRepoCreation
}

// CanCreateRepo returns if user login can create a repository
// NOTE: functions calling this assume a failure due to repository count limit; if new checks are added, those functions should be revised
func (u *User) CanCreateRepo() bool {
// CanCreateRepoIn checks whether the doer(u) can create a repository in the owner
func (u *User) CanCreateRepoIn(owner *User) bool {
if u.IsAdmin {
return true
}
if u.MaxRepoCreation <= -1 {
if setting.Repository.MaxCreationLimit <= -1 {
const noLimit = -1
if owner.MaxRepoCreation == noLimit {
if setting.Repository.MaxCreationLimit == noLimit {
return true
}
return u.NumRepos < setting.Repository.MaxCreationLimit
return owner.NumRepos < setting.Repository.MaxCreationLimit
}
return u.NumRepos < u.MaxRepoCreation
return owner.NumRepos < owner.MaxRepoCreation
}

// CanCreateOrganization returns true if user can create organisation.
Expand All @@ -272,13 +272,11 @@ 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 {
func (u *User) CanForkRepoIn(owner *User) bool {
if setting.Repository.AllowForkWithoutMaximumLimit {
return true
}
return u.CanCreateRepo()
return u.CanCreateRepoIn(owner)
}

// CanImportLocal returns true if user can migrate repository by local path.
Expand Down
35 changes: 35 additions & 0 deletions models/user/user_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/modules/timeutil"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -616,3 +617,37 @@ func TestGetInactiveUsers(t *testing.T) {
assert.NoError(t, err)
assert.Empty(t, users)
}

func TestCanCreateRepo(t *testing.T) {
defer test.MockVariableValue(&setting.Repository.MaxCreationLimit)()
const noLimit = -1
doerNormal := &user_model.User{}
doerAdmin := &user_model.User{IsAdmin: true}
t.Run("NoGlobalLimit", func(t *testing.T) {
setting.Repository.MaxCreationLimit = noLimit

assert.True(t, doerNormal.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: noLimit}))
assert.False(t, doerNormal.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: 0}))
assert.True(t, doerNormal.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: 100}))

assert.True(t, doerAdmin.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: noLimit}))
assert.True(t, doerAdmin.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: 0}))
assert.True(t, doerAdmin.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: 100}))
})

t.Run("GlobalLimit50", func(t *testing.T) {
setting.Repository.MaxCreationLimit = 50

assert.True(t, doerNormal.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: noLimit}))
assert.False(t, doerNormal.CanCreateRepoIn(&user_model.User{NumRepos: 60, MaxRepoCreation: noLimit})) // limited by global limit
assert.False(t, doerNormal.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: 0}))
assert.True(t, doerNormal.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: 100}))
assert.True(t, doerNormal.CanCreateRepoIn(&user_model.User{NumRepos: 60, MaxRepoCreation: 100}))

assert.True(t, doerAdmin.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: noLimit}))
assert.True(t, doerAdmin.CanCreateRepoIn(&user_model.User{NumRepos: 60, MaxRepoCreation: noLimit}))
assert.True(t, doerAdmin.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: 0}))
assert.True(t, doerAdmin.CanCreateRepoIn(&user_model.User{NumRepos: 10, MaxRepoCreation: 100}))
assert.True(t, doerAdmin.CanCreateRepoIn(&user_model.User{NumRepos: 60, MaxRepoCreation: 100}))
})
}
17 changes: 6 additions & 11 deletions routers/web/repo/fork.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,17 @@ func getForkRepository(ctx *context.Context) *repo_model.Repository {
ctx.Data["CanForkToUser"] = canForkToUser
ctx.Data["Orgs"] = orgs

// TODO: this message should only be shown for the "current doer" when it is selected, just like the "new repo" page.
// msg := ctx.TrN(maxCreationLimit, "repo.form.reach_limit_of_creation_1", "repo.form.reach_limit_of_creation_n", ctx.Doer.MaxCreationLimit())

if canForkToUser {
ctx.Data["ContextUser"] = ctx.Doer
ctx.Data["CanForkRepoInNewOwner"] = true
} else if len(orgs) > 0 {
ctx.Data["ContextUser"] = orgs[0]
ctx.Data["CanForkRepoInNewOwner"] = true
} else {
ctx.Data["CanForkRepo"] = false
ctx.Data["CanForkRepoInNewOwner"] = false
ctx.Flash.Error(ctx.Tr("repo.fork_no_valid_owners"), true)
return nil
}
Expand All @@ -120,15 +125,6 @@ func getForkRepository(ctx *context.Context) *repo_model.Repository {
// Fork render repository fork page
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.Flash.Error(msg, true)
}

getForkRepository(ctx)
if ctx.Written() {
return
Expand All @@ -141,7 +137,6 @@ func Fork(ctx *context.Context) {
func ForkPost(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.CreateRepoForm)
ctx.Data["Title"] = ctx.Tr("new_fork")
ctx.Data["CanForkRepo"] = true

ctxUser := checkContextUser(ctx, form.UID)
if ctx.Written() {
Expand Down
16 changes: 6 additions & 10 deletions routers/web/repo/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,17 +87,13 @@ func checkContextUser(ctx *context.Context, uid int64) *user_model.User {
return nil
}

if !ctx.Doer.IsAdmin {
orgsAvailable := []*organization.Organization{}
for i := 0; i < len(orgs); i++ {
if orgs[i].CanCreateRepo() {
orgsAvailable = append(orgsAvailable, orgs[i])
}
var orgsAvailable []*organization.Organization
for i := 0; i < len(orgs); i++ {
if ctx.Doer.CanCreateRepoIn(orgs[i].AsUser()) {
orgsAvailable = append(orgsAvailable, orgs[i])
}
ctx.Data["Orgs"] = orgsAvailable
} else {
ctx.Data["Orgs"] = orgs
}
ctx.Data["Orgs"] = orgsAvailable

// Not equal means current user is an organization.
if uid == ctx.Doer.ID || uid == 0 {
Expand Down Expand Up @@ -154,7 +150,7 @@ func createCommon(ctx *context.Context) {
ctx.Data["Licenses"] = repo_module.Licenses
ctx.Data["Readmes"] = repo_module.Readmes
ctx.Data["IsForcedPrivate"] = setting.Repository.ForcePrivate
ctx.Data["CanCreateRepoInDoer"] = ctx.Doer.CanCreateRepo()
ctx.Data["CanCreateRepoInDoer"] = ctx.Doer.CanCreateRepoIn(ctx.Doer)
ctx.Data["MaxCreationLimitOfDoer"] = ctx.Doer.MaxCreationLimit()
ctx.Data["SupportedObjectFormats"] = git.DefaultFeatures().SupportedObjectFormats
ctx.Data["DefaultObjectFormat"] = git.Sha1ObjectFormat
Expand Down
3 changes: 2 additions & 1 deletion routers/web/repo/setting/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ func SettingsCtxData(ctx *context.Context) {
ctx.Data["DisableNewPushMirrors"] = setting.Mirror.DisableNewPush
ctx.Data["DefaultMirrorInterval"] = setting.Mirror.DefaultInterval
ctx.Data["MinimumMirrorInterval"] = setting.Mirror.MinInterval
ctx.Data["CanConvertFork"] = ctx.Repo.Repository.IsFork && ctx.Doer.CanCreateRepoIn(ctx.Repo.Repository.Owner)

signing, _ := asymkey_service.SigningKey(ctx, ctx.Repo.Repository.RepoPath())
ctx.Data["SigningKeyAvailable"] = len(signing) > 0
Expand Down Expand Up @@ -786,7 +787,7 @@ func handleSettingsPostConvertFork(ctx *context.Context) {
return
}

if !ctx.Repo.Owner.CanCreateRepo() {
if !ctx.Doer.CanForkRepoIn(ctx.Repo.Owner) {
maxCreationLimit := ctx.Repo.Owner.MaxCreationLimit()
msg := ctx.TrN(maxCreationLimit, "repo.form.reach_limit_of_creation_1", "repo.form.reach_limit_of_creation_n", maxCreationLimit)
ctx.Flash.Error(msg)
Expand Down
18 changes: 9 additions & 9 deletions services/repository/adopt.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,17 @@ import (
)

// AdoptRepository adopts pre-existing repository files for the user/organization.
func AdoptRepository(ctx context.Context, doer, u *user_model.User, opts CreateRepoOptions) (*repo_model.Repository, error) {
if !doer.IsAdmin && !u.CanCreateRepo() {
func AdoptRepository(ctx context.Context, doer, owner *user_model.User, opts CreateRepoOptions) (*repo_model.Repository, error) {
if !doer.CanCreateRepoIn(owner) {
return nil, repo_model.ErrReachLimitOfRepo{
Limit: u.MaxRepoCreation,
Limit: owner.MaxRepoCreation,
}
}

repo := &repo_model.Repository{
OwnerID: u.ID,
Owner: u,
OwnerName: u.Name,
OwnerID: owner.ID,
Owner: owner,
OwnerName: owner.Name,
Name: opts.Name,
LowerName: strings.ToLower(opts.Name),
Description: opts.Description,
Expand All @@ -60,12 +60,12 @@ func AdoptRepository(ctx context.Context, doer, u *user_model.User, opts CreateR
}
if !isExist {
return repo_model.ErrRepoNotExist{
OwnerName: u.Name,
OwnerName: owner.Name,
Name: repo.Name,
}
}

if err := CreateRepositoryByExample(ctx, doer, u, repo, true, false); err != nil {
if err := CreateRepositoryByExample(ctx, doer, owner, repo, true, false); err != nil {
return err
}

Expand Down Expand Up @@ -100,7 +100,7 @@ func AdoptRepository(ctx context.Context, doer, u *user_model.User, opts CreateR
}
return nil, err
}
notify_service.AdoptRepository(ctx, doer, u, repo)
notify_service.AdoptRepository(ctx, doer, owner, repo)

return repo, nil
}
Expand Down
24 changes: 12 additions & 12 deletions services/repository/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,10 +200,10 @@ func initRepository(ctx context.Context, u *user_model.User, repo *repo_model.Re
}

// CreateRepositoryDirectly creates a repository for the user/organization.
func CreateRepositoryDirectly(ctx context.Context, doer, u *user_model.User, opts CreateRepoOptions) (*repo_model.Repository, error) {
if !doer.IsAdmin && !u.CanCreateRepo() {
func CreateRepositoryDirectly(ctx context.Context, doer, owner *user_model.User, opts CreateRepoOptions) (*repo_model.Repository, error) {
if !doer.CanCreateRepoIn(owner) {
return nil, repo_model.ErrReachLimitOfRepo{
Limit: u.MaxRepoCreation,
Limit: owner.MaxRepoCreation,
}
}

Expand All @@ -223,9 +223,9 @@ func CreateRepositoryDirectly(ctx context.Context, doer, u *user_model.User, opt
}

repo := &repo_model.Repository{
OwnerID: u.ID,
Owner: u,
OwnerName: u.Name,
OwnerID: owner.ID,
Owner: owner,
OwnerName: owner.Name,
Name: opts.Name,
LowerName: strings.ToLower(opts.Name),
Description: opts.Description,
Expand All @@ -247,7 +247,7 @@ func CreateRepositoryDirectly(ctx context.Context, doer, u *user_model.User, opt
var rollbackRepo *repo_model.Repository

if err := db.WithTx(ctx, func(ctx context.Context) error {
if err := CreateRepositoryByExample(ctx, doer, u, repo, false, false); err != nil {
if err := CreateRepositoryByExample(ctx, doer, owner, repo, false, false); err != nil {
return err
}

Expand All @@ -271,7 +271,7 @@ func CreateRepositoryDirectly(ctx context.Context, doer, u *user_model.User, opt
// So we will now fail and delegate to other functionality to adopt or delete
log.Error("Files already exist in %s and we are not going to adopt or delete.", repo.FullName())
return repo_model.ErrRepoFilesAlreadyExist{
Uname: u.Name,
Uname: owner.Name,
Name: repo.Name,
}
}
Expand All @@ -280,7 +280,7 @@ func CreateRepositoryDirectly(ctx context.Context, doer, u *user_model.User, opt
if err2 := gitrepo.DeleteRepository(ctx, repo); err2 != nil {
log.Error("initRepository: %v", err)
return fmt.Errorf(
"delete repo directory %s/%s failed(2): %v", u.Name, repo.Name, err2)
"delete repo directory %s/%s failed(2): %v", owner.Name, repo.Name, err2)
}
return fmt.Errorf("initRepository: %w", err)
}
Expand All @@ -289,7 +289,7 @@ func CreateRepositoryDirectly(ctx context.Context, doer, u *user_model.User, opt
if len(opts.IssueLabels) > 0 {
if err = repo_module.InitializeLabels(ctx, repo.ID, opts.IssueLabels, false); err != nil {
rollbackRepo = repo
rollbackRepo.OwnerID = u.ID
rollbackRepo.OwnerID = owner.ID
return fmt.Errorf("InitializeLabels: %w", err)
}
}
Expand All @@ -302,7 +302,7 @@ func CreateRepositoryDirectly(ctx context.Context, doer, u *user_model.User, opt
RunStdString(ctx, &git.RunOpts{Dir: repo.RepoPath()}); err != nil {
log.Error("CreateRepository(git update-server-info) in %v: Stdout: %s\nError: %v", repo, stdout, err)
rollbackRepo = repo
rollbackRepo.OwnerID = u.ID
rollbackRepo.OwnerID = owner.ID
return fmt.Errorf("CreateRepository(git update-server-info): %w", err)
}

Expand All @@ -315,7 +315,7 @@ func CreateRepositoryDirectly(ctx context.Context, doer, u *user_model.User, opt
if err != nil {
log.Error("CreateRepository(git rev-parse HEAD) in %v: Stdout: %s\nError: %v", repo, stdout, err)
rollbackRepo = repo
rollbackRepo.OwnerID = u.ID
rollbackRepo.OwnerID = owner.ID
return fmt.Errorf("CreateRepository(git rev-parse HEAD): %w", err)
}
if err := repo_model.UpdateRepoLicenses(ctx, repo, stdout, licenses); err != nil {
Expand Down
2 changes: 1 addition & 1 deletion services/repository/fork.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func ForkRepository(ctx context.Context, doer, owner *user_model.User, opts Fork
}

// Fork is prohibited, if user has reached maximum limit of repositories
if !owner.CanForkRepo() {
if !doer.CanForkRepoIn(owner) {
return nil, repo_model.ErrReachLimitOfRepo{
Limit: owner.MaxRepoCreation,
}
Expand Down
2 changes: 1 addition & 1 deletion services/repository/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func GenerateProtectedBranch(ctx context.Context, templateRepo, generateRepo *re

// GenerateRepository generates a repository from a template
func GenerateRepository(ctx context.Context, doer, owner *user_model.User, templateRepo *repo_model.Repository, opts GenerateRepoOptions) (_ *repo_model.Repository, err error) {
if !doer.IsAdmin && !owner.CanCreateRepo() {
if !doer.CanCreateRepoIn(owner) {
return nil, repo_model.ErrReachLimitOfRepo{
Limit: owner.MaxRepoCreation,
}
Expand Down
4 changes: 2 additions & 2 deletions services/repository/transfer.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func AcceptTransferOwnership(ctx context.Context, repo *repo_model.Repository, d
return err
}

if !doer.IsAdmin && !repoTransfer.Recipient.CanCreateRepo() {
if !doer.CanCreateRepoIn(repoTransfer.Recipient) {
limit := util.Iif(repoTransfer.Recipient.MaxRepoCreation >= 0, repoTransfer.Recipient.MaxRepoCreation, setting.Repository.MaxCreationLimit)
return LimitReachedError{Limit: limit}
}
Expand Down Expand Up @@ -416,7 +416,7 @@ func StartRepositoryTransfer(ctx context.Context, doer, newOwner *user_model.Use
return err
}

if !doer.IsAdmin && !newOwner.CanCreateRepo() {
if !doer.CanForkRepoIn(newOwner) {
limit := util.Iif(newOwner.MaxRepoCreation >= 0, newOwner.MaxRepoCreation, setting.Repository.MaxCreationLimit)
return LimitReachedError{Limit: limit}
}
Expand Down
Loading