mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 21:28:11 +09:00 
			
		
		
		
	Move AddCollabrator and CreateRepositoryByExample to service layer (#32419)
- [x] Move `CreateRepositoryByExample` to service layer - [x] Move `AddCollabrator` to service layer - [x] Add a new parameter for `AddCollabrator` so that changing mode immediately after that will become unnecessary.
This commit is contained in:
		| @@ -60,3 +60,6 @@ func ParseAccessMode(permission string, allowed ...AccessMode) AccessMode { | |||||||
| 	} | 	} | ||||||
| 	return util.Iif(slices.Contains(allowed, m), m, AccessModeNone) | 	return util.Iif(slices.Contains(allowed, m), m, AccessModeNone) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // ErrInvalidAccessMode is returned when an invalid access mode is used | ||||||
|  | var ErrInvalidAccessMode = util.NewInvalidArgumentErrorf("Invalid access mode") | ||||||
|   | |||||||
| @@ -1,48 +0,0 @@ | |||||||
| // Copyright 2022 The Gitea Authors. All rights reserved. |  | ||||||
| // SPDX-License-Identifier: MIT |  | ||||||
|  |  | ||||||
| package repository |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"context" |  | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/db" |  | ||||||
| 	"code.gitea.io/gitea/models/perm" |  | ||||||
| 	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" |  | ||||||
|  |  | ||||||
| 	"xorm.io/builder" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func AddCollaborator(ctx context.Context, repo *repo_model.Repository, u *user_model.User) error { |  | ||||||
| 	if err := repo.LoadOwner(ctx); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if user_model.IsUserBlockedBy(ctx, u, repo.OwnerID) || user_model.IsUserBlockedBy(ctx, repo.Owner, u.ID) { |  | ||||||
| 		return user_model.ErrBlockedUser |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return db.WithTx(ctx, func(ctx context.Context) error { |  | ||||||
| 		has, err := db.Exist[repo_model.Collaboration](ctx, builder.Eq{ |  | ||||||
| 			"repo_id": repo.ID, |  | ||||||
| 			"user_id": u.ID, |  | ||||||
| 		}) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} else if has { |  | ||||||
| 			return nil |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		if err = db.Insert(ctx, &repo_model.Collaboration{ |  | ||||||
| 			RepoID: repo.ID, |  | ||||||
| 			UserID: u.ID, |  | ||||||
| 			Mode:   perm.AccessModeWrite, |  | ||||||
| 		}); err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		return access_model.RecalculateUserAccess(ctx, repo, u.ID) |  | ||||||
| 	}) |  | ||||||
| } |  | ||||||
| @@ -1,280 +0,0 @@ | |||||||
| // Copyright 2019 The Gitea Authors. All rights reserved. |  | ||||||
| // SPDX-License-Identifier: MIT |  | ||||||
|  |  | ||||||
| package repository |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"testing" |  | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/db" |  | ||||||
| 	"code.gitea.io/gitea/models/organization" |  | ||||||
| 	perm_model "code.gitea.io/gitea/models/perm" |  | ||||||
| 	access_model "code.gitea.io/gitea/models/perm/access" |  | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" |  | ||||||
| 	"code.gitea.io/gitea/models/unit" |  | ||||||
| 	"code.gitea.io/gitea/models/unittest" |  | ||||||
| 	user_model "code.gitea.io/gitea/models/user" |  | ||||||
|  |  | ||||||
| 	"github.com/stretchr/testify/assert" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func TestRepository_AddCollaborator(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
|  |  | ||||||
| 	testSuccess := func(repoID, userID int64) { |  | ||||||
| 		repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID}) |  | ||||||
| 		assert.NoError(t, repo.LoadOwner(db.DefaultContext)) |  | ||||||
| 		user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userID}) |  | ||||||
| 		assert.NoError(t, AddCollaborator(db.DefaultContext, repo, user)) |  | ||||||
| 		unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: repoID}, &user_model.User{ID: userID}) |  | ||||||
| 	} |  | ||||||
| 	testSuccess(1, 4) |  | ||||||
| 	testSuccess(1, 4) |  | ||||||
| 	testSuccess(3, 4) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestRepoPermissionPublicNonOrgRepo(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
|  |  | ||||||
| 	// public non-organization repo |  | ||||||
| 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4}) |  | ||||||
| 	assert.NoError(t, repo.LoadUnits(db.DefaultContext)) |  | ||||||
|  |  | ||||||
| 	// plain user |  | ||||||
| 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) |  | ||||||
| 	perm, err := access_model.GetUserRepoPermission(db.DefaultContext, repo, user) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	for _, unit := range repo.Units { |  | ||||||
| 		assert.True(t, perm.CanRead(unit.Type)) |  | ||||||
| 		assert.False(t, perm.CanWrite(unit.Type)) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// change to collaborator |  | ||||||
| 	assert.NoError(t, AddCollaborator(db.DefaultContext, repo, user)) |  | ||||||
| 	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	for _, unit := range repo.Units { |  | ||||||
| 		assert.True(t, perm.CanRead(unit.Type)) |  | ||||||
| 		assert.True(t, perm.CanWrite(unit.Type)) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// collaborator |  | ||||||
| 	collaborator := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) |  | ||||||
| 	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, collaborator) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	for _, unit := range repo.Units { |  | ||||||
| 		assert.True(t, perm.CanRead(unit.Type)) |  | ||||||
| 		assert.True(t, perm.CanWrite(unit.Type)) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// owner |  | ||||||
| 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}) |  | ||||||
| 	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, owner) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	for _, unit := range repo.Units { |  | ||||||
| 		assert.True(t, perm.CanRead(unit.Type)) |  | ||||||
| 		assert.True(t, perm.CanWrite(unit.Type)) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// admin |  | ||||||
| 	admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) |  | ||||||
| 	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, admin) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	for _, unit := range repo.Units { |  | ||||||
| 		assert.True(t, perm.CanRead(unit.Type)) |  | ||||||
| 		assert.True(t, perm.CanWrite(unit.Type)) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestRepoPermissionPrivateNonOrgRepo(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
|  |  | ||||||
| 	// private non-organization repo |  | ||||||
| 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) |  | ||||||
| 	assert.NoError(t, repo.LoadUnits(db.DefaultContext)) |  | ||||||
|  |  | ||||||
| 	// plain user |  | ||||||
| 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) |  | ||||||
| 	perm, err := access_model.GetUserRepoPermission(db.DefaultContext, repo, user) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	for _, unit := range repo.Units { |  | ||||||
| 		assert.False(t, perm.CanRead(unit.Type)) |  | ||||||
| 		assert.False(t, perm.CanWrite(unit.Type)) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// change to collaborator to default write access |  | ||||||
| 	assert.NoError(t, AddCollaborator(db.DefaultContext, repo, user)) |  | ||||||
| 	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	for _, unit := range repo.Units { |  | ||||||
| 		assert.True(t, perm.CanRead(unit.Type)) |  | ||||||
| 		assert.True(t, perm.CanWrite(unit.Type)) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	assert.NoError(t, repo_model.ChangeCollaborationAccessMode(db.DefaultContext, repo, user.ID, perm_model.AccessModeRead)) |  | ||||||
| 	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	for _, unit := range repo.Units { |  | ||||||
| 		assert.True(t, perm.CanRead(unit.Type)) |  | ||||||
| 		assert.False(t, perm.CanWrite(unit.Type)) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// owner |  | ||||||
| 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) |  | ||||||
| 	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, owner) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	for _, unit := range repo.Units { |  | ||||||
| 		assert.True(t, perm.CanRead(unit.Type)) |  | ||||||
| 		assert.True(t, perm.CanWrite(unit.Type)) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// admin |  | ||||||
| 	admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) |  | ||||||
| 	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, admin) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	for _, unit := range repo.Units { |  | ||||||
| 		assert.True(t, perm.CanRead(unit.Type)) |  | ||||||
| 		assert.True(t, perm.CanWrite(unit.Type)) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestRepoPermissionPublicOrgRepo(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
|  |  | ||||||
| 	// public organization repo |  | ||||||
| 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 32}) |  | ||||||
| 	assert.NoError(t, repo.LoadUnits(db.DefaultContext)) |  | ||||||
|  |  | ||||||
| 	// plain user |  | ||||||
| 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}) |  | ||||||
| 	perm, err := access_model.GetUserRepoPermission(db.DefaultContext, repo, user) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	for _, unit := range repo.Units { |  | ||||||
| 		assert.True(t, perm.CanRead(unit.Type)) |  | ||||||
| 		assert.False(t, perm.CanWrite(unit.Type)) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// change to collaborator to default write access |  | ||||||
| 	assert.NoError(t, AddCollaborator(db.DefaultContext, repo, user)) |  | ||||||
| 	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	for _, unit := range repo.Units { |  | ||||||
| 		assert.True(t, perm.CanRead(unit.Type)) |  | ||||||
| 		assert.True(t, perm.CanWrite(unit.Type)) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	assert.NoError(t, repo_model.ChangeCollaborationAccessMode(db.DefaultContext, repo, user.ID, perm_model.AccessModeRead)) |  | ||||||
| 	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	for _, unit := range repo.Units { |  | ||||||
| 		assert.True(t, perm.CanRead(unit.Type)) |  | ||||||
| 		assert.False(t, perm.CanWrite(unit.Type)) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// org member team owner |  | ||||||
| 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) |  | ||||||
| 	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, owner) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	for _, unit := range repo.Units { |  | ||||||
| 		assert.True(t, perm.CanRead(unit.Type)) |  | ||||||
| 		assert.True(t, perm.CanWrite(unit.Type)) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// org member team tester |  | ||||||
| 	member := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 15}) |  | ||||||
| 	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, member) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	for _, unit := range repo.Units { |  | ||||||
| 		assert.True(t, perm.CanRead(unit.Type)) |  | ||||||
| 	} |  | ||||||
| 	assert.True(t, perm.CanWrite(unit.TypeIssues)) |  | ||||||
| 	assert.False(t, perm.CanWrite(unit.TypeCode)) |  | ||||||
|  |  | ||||||
| 	// admin |  | ||||||
| 	admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) |  | ||||||
| 	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, admin) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	for _, unit := range repo.Units { |  | ||||||
| 		assert.True(t, perm.CanRead(unit.Type)) |  | ||||||
| 		assert.True(t, perm.CanWrite(unit.Type)) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestRepoPermissionPrivateOrgRepo(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
|  |  | ||||||
| 	// private organization repo |  | ||||||
| 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 24}) |  | ||||||
| 	assert.NoError(t, repo.LoadUnits(db.DefaultContext)) |  | ||||||
|  |  | ||||||
| 	// plain user |  | ||||||
| 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}) |  | ||||||
| 	perm, err := access_model.GetUserRepoPermission(db.DefaultContext, repo, user) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	for _, unit := range repo.Units { |  | ||||||
| 		assert.False(t, perm.CanRead(unit.Type)) |  | ||||||
| 		assert.False(t, perm.CanWrite(unit.Type)) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// change to collaborator to default write access |  | ||||||
| 	assert.NoError(t, AddCollaborator(db.DefaultContext, repo, user)) |  | ||||||
| 	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	for _, unit := range repo.Units { |  | ||||||
| 		assert.True(t, perm.CanRead(unit.Type)) |  | ||||||
| 		assert.True(t, perm.CanWrite(unit.Type)) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	assert.NoError(t, repo_model.ChangeCollaborationAccessMode(db.DefaultContext, repo, user.ID, perm_model.AccessModeRead)) |  | ||||||
| 	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	for _, unit := range repo.Units { |  | ||||||
| 		assert.True(t, perm.CanRead(unit.Type)) |  | ||||||
| 		assert.False(t, perm.CanWrite(unit.Type)) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// org member team owner |  | ||||||
| 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 15}) |  | ||||||
| 	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, owner) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	for _, unit := range repo.Units { |  | ||||||
| 		assert.True(t, perm.CanRead(unit.Type)) |  | ||||||
| 		assert.True(t, perm.CanWrite(unit.Type)) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// update team information and then check permission |  | ||||||
| 	team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 5}) |  | ||||||
| 	err = organization.UpdateTeamUnits(db.DefaultContext, team, nil) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, owner) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	for _, unit := range repo.Units { |  | ||||||
| 		assert.True(t, perm.CanRead(unit.Type)) |  | ||||||
| 		assert.True(t, perm.CanWrite(unit.Type)) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// org member team tester |  | ||||||
| 	tester := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) |  | ||||||
| 	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, tester) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	assert.True(t, perm.CanWrite(unit.TypeIssues)) |  | ||||||
| 	assert.False(t, perm.CanWrite(unit.TypeCode)) |  | ||||||
| 	assert.False(t, perm.CanRead(unit.TypeCode)) |  | ||||||
|  |  | ||||||
| 	// org member team reviewer |  | ||||||
| 	reviewer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 20}) |  | ||||||
| 	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, reviewer) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	assert.False(t, perm.CanRead(unit.TypeIssues)) |  | ||||||
| 	assert.False(t, perm.CanWrite(unit.TypeCode)) |  | ||||||
| 	assert.True(t, perm.CanRead(unit.TypeCode)) |  | ||||||
|  |  | ||||||
| 	// admin |  | ||||||
| 	admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) |  | ||||||
| 	perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, admin) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	for _, unit := range repo.Units { |  | ||||||
| 		assert.True(t, perm.CanRead(unit.Type)) |  | ||||||
| 		assert.True(t, perm.CanWrite(unit.Type)) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @@ -11,160 +11,17 @@ import ( | |||||||
| 	"path/filepath" | 	"path/filepath" | ||||||
| 	"strings" | 	"strings" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" |  | ||||||
| 	activities_model "code.gitea.io/gitea/models/activities" | 	activities_model "code.gitea.io/gitea/models/activities" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	git_model "code.gitea.io/gitea/models/git" | 	git_model "code.gitea.io/gitea/models/git" | ||||||
| 	"code.gitea.io/gitea/models/organization" |  | ||||||
| 	"code.gitea.io/gitea/models/perm" |  | ||||||
| 	access_model "code.gitea.io/gitea/models/perm/access" | 	access_model "code.gitea.io/gitea/models/perm/access" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unit" |  | ||||||
| 	user_model "code.gitea.io/gitea/models/user" |  | ||||||
| 	"code.gitea.io/gitea/models/webhook" |  | ||||||
| 	issue_indexer "code.gitea.io/gitea/modules/indexer/issues" | 	issue_indexer "code.gitea.io/gitea/modules/indexer/issues" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| 	"code.gitea.io/gitea/modules/setting" |  | ||||||
| 	api "code.gitea.io/gitea/modules/structs" | 	api "code.gitea.io/gitea/modules/structs" | ||||||
| 	"code.gitea.io/gitea/modules/util" | 	"code.gitea.io/gitea/modules/util" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // CreateRepositoryByExample creates a repository for the user/organization. |  | ||||||
| func CreateRepositoryByExample(ctx context.Context, doer, u *user_model.User, repo *repo_model.Repository, overwriteOrAdopt, isFork bool) (err error) { |  | ||||||
| 	if err = repo_model.IsUsableRepoName(repo.Name); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	has, err := repo_model.IsRepositoryModelExist(ctx, u, repo.Name) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return fmt.Errorf("IsRepositoryExist: %w", err) |  | ||||||
| 	} else if has { |  | ||||||
| 		return repo_model.ErrRepoAlreadyExist{ |  | ||||||
| 			Uname: u.Name, |  | ||||||
| 			Name:  repo.Name, |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	repoPath := repo_model.RepoPath(u.Name, repo.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 { |  | ||||||
| 		log.Error("Files already exist in %s and we are not going to adopt or delete.", repoPath) |  | ||||||
| 		return repo_model.ErrRepoFilesAlreadyExist{ |  | ||||||
| 			Uname: u.Name, |  | ||||||
| 			Name:  repo.Name, |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if err = db.Insert(ctx, repo); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	if err = repo_model.DeleteRedirect(ctx, u.ID, repo.Name); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// insert units for repo |  | ||||||
| 	defaultUnits := unit.DefaultRepoUnits |  | ||||||
| 	if isFork { |  | ||||||
| 		defaultUnits = unit.DefaultForkRepoUnits |  | ||||||
| 	} |  | ||||||
| 	units := make([]repo_model.RepoUnit, 0, len(defaultUnits)) |  | ||||||
| 	for _, tp := range defaultUnits { |  | ||||||
| 		if tp == unit.TypeIssues { |  | ||||||
| 			units = append(units, repo_model.RepoUnit{ |  | ||||||
| 				RepoID: repo.ID, |  | ||||||
| 				Type:   tp, |  | ||||||
| 				Config: &repo_model.IssuesConfig{ |  | ||||||
| 					EnableTimetracker:                setting.Service.DefaultEnableTimetracking, |  | ||||||
| 					AllowOnlyContributorsToTrackTime: setting.Service.DefaultAllowOnlyContributorsToTrackTime, |  | ||||||
| 					EnableDependencies:               setting.Service.DefaultEnableDependencies, |  | ||||||
| 				}, |  | ||||||
| 			}) |  | ||||||
| 		} else if tp == unit.TypePullRequests { |  | ||||||
| 			units = append(units, repo_model.RepoUnit{ |  | ||||||
| 				RepoID: repo.ID, |  | ||||||
| 				Type:   tp, |  | ||||||
| 				Config: &repo_model.PullRequestsConfig{ |  | ||||||
| 					AllowMerge: true, AllowRebase: true, AllowRebaseMerge: true, AllowSquash: true, AllowFastForwardOnly: true, |  | ||||||
| 					DefaultMergeStyle: repo_model.MergeStyle(setting.Repository.PullRequest.DefaultMergeStyle), |  | ||||||
| 					AllowRebaseUpdate: true, |  | ||||||
| 				}, |  | ||||||
| 			}) |  | ||||||
| 		} else if tp == unit.TypeProjects { |  | ||||||
| 			units = append(units, repo_model.RepoUnit{ |  | ||||||
| 				RepoID: repo.ID, |  | ||||||
| 				Type:   tp, |  | ||||||
| 				Config: &repo_model.ProjectsConfig{ProjectsMode: repo_model.ProjectsModeAll}, |  | ||||||
| 			}) |  | ||||||
| 		} else { |  | ||||||
| 			units = append(units, repo_model.RepoUnit{ |  | ||||||
| 				RepoID: repo.ID, |  | ||||||
| 				Type:   tp, |  | ||||||
| 			}) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if err = db.Insert(ctx, units); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// Remember visibility preference. |  | ||||||
| 	u.LastRepoVisibility = repo.IsPrivate |  | ||||||
| 	if err = user_model.UpdateUserCols(ctx, u, "last_repo_visibility"); err != nil { |  | ||||||
| 		return fmt.Errorf("UpdateUserCols: %w", err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if err = user_model.IncrUserRepoNum(ctx, u.ID); err != nil { |  | ||||||
| 		return fmt.Errorf("IncrUserRepoNum: %w", err) |  | ||||||
| 	} |  | ||||||
| 	u.NumRepos++ |  | ||||||
|  |  | ||||||
| 	// Give access to all members in teams with access to all repositories. |  | ||||||
| 	if u.IsOrganization() { |  | ||||||
| 		teams, err := organization.FindOrgTeams(ctx, u.ID) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return fmt.Errorf("FindOrgTeams: %w", err) |  | ||||||
| 		} |  | ||||||
| 		for _, t := range teams { |  | ||||||
| 			if t.IncludesAllRepositories { |  | ||||||
| 				if err := models.AddRepository(ctx, t, repo); err != nil { |  | ||||||
| 					return fmt.Errorf("AddRepository: %w", err) |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		if isAdmin, err := access_model.IsUserRepoAdmin(ctx, repo, doer); err != nil { |  | ||||||
| 			return fmt.Errorf("IsUserRepoAdmin: %w", err) |  | ||||||
| 		} else if !isAdmin { |  | ||||||
| 			// Make creator repo admin if it wasn't assigned automatically |  | ||||||
| 			if err = AddCollaborator(ctx, repo, doer); err != nil { |  | ||||||
| 				return fmt.Errorf("AddCollaborator: %w", err) |  | ||||||
| 			} |  | ||||||
| 			if err = repo_model.ChangeCollaborationAccessMode(ctx, repo, doer.ID, perm.AccessModeAdmin); err != nil { |  | ||||||
| 				return fmt.Errorf("ChangeCollaborationAccessModeCtx: %w", err) |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} else if err = access_model.RecalculateAccesses(ctx, repo); err != nil { |  | ||||||
| 		// Organization automatically called this in AddRepository method. |  | ||||||
| 		return fmt.Errorf("RecalculateAccesses: %w", err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if setting.Service.AutoWatchNewRepos { |  | ||||||
| 		if err = repo_model.WatchRepo(ctx, doer, repo, true); err != nil { |  | ||||||
| 			return fmt.Errorf("WatchRepo: %w", err) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if err = webhook.CopyDefaultWebhooksToRepo(ctx, repo.ID); err != nil { |  | ||||||
| 		return fmt.Errorf("CopyDefaultWebhooksToRepo: %w", err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| const notRegularFileMode = os.ModeSymlink | os.ModeNamedPipe | os.ModeSocket | os.ModeDevice | os.ModeCharDevice | os.ModeIrregular | const notRegularFileMode = os.ModeSymlink | os.ModeNamedPipe | os.ModeSocket | os.ModeDevice | os.ModeCharDevice | os.ModeIrregular | ||||||
|  |  | ||||||
| // getDirectorySize returns the disk consumption for a given path | // getDirectorySize returns the disk consumption for a given path | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ import ( | |||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
|  |  | ||||||
|  | 	_ "code.gitea.io/gitea/models" | ||||||
| 	_ "code.gitea.io/gitea/models/actions" | 	_ "code.gitea.io/gitea/models/actions" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1172,7 +1172,7 @@ func Routes() *web.Router { | |||||||
| 					m.Get("", reqAnyRepoReader(), repo.ListCollaborators) | 					m.Get("", reqAnyRepoReader(), repo.ListCollaborators) | ||||||
| 					m.Group("/{collaborator}", func() { | 					m.Group("/{collaborator}", func() { | ||||||
| 						m.Combo("").Get(reqAnyRepoReader(), repo.IsCollaborator). | 						m.Combo("").Get(reqAnyRepoReader(), repo.IsCollaborator). | ||||||
| 							Put(reqAdmin(), bind(api.AddCollaboratorOption{}), repo.AddCollaborator). | 							Put(reqAdmin(), bind(api.AddCollaboratorOption{}), repo.AddOrUpdateCollaborator). | ||||||
| 							Delete(reqAdmin(), repo.DeleteCollaborator) | 							Delete(reqAdmin(), repo.DeleteCollaborator) | ||||||
| 						m.Get("/permission", repo.GetRepoPermissions) | 						m.Get("/permission", repo.GetRepoPermissions) | ||||||
| 					}) | 					}) | ||||||
|   | |||||||
| @@ -12,7 +12,6 @@ import ( | |||||||
| 	access_model "code.gitea.io/gitea/models/perm/access" | 	access_model "code.gitea.io/gitea/models/perm/access" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	repo_module "code.gitea.io/gitea/modules/repository" |  | ||||||
| 	api "code.gitea.io/gitea/modules/structs" | 	api "code.gitea.io/gitea/modules/structs" | ||||||
| 	"code.gitea.io/gitea/modules/web" | 	"code.gitea.io/gitea/modules/web" | ||||||
| 	"code.gitea.io/gitea/routers/api/v1/utils" | 	"code.gitea.io/gitea/routers/api/v1/utils" | ||||||
| @@ -123,11 +122,11 @@ func IsCollaborator(ctx *context.APIContext) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // AddCollaborator add a collaborator to a repository | // AddOrUpdateCollaborator add or update a collaborator to a repository | ||||||
| func AddCollaborator(ctx *context.APIContext) { | func AddOrUpdateCollaborator(ctx *context.APIContext) { | ||||||
| 	// swagger:operation PUT /repos/{owner}/{repo}/collaborators/{collaborator} repository repoAddCollaborator | 	// swagger:operation PUT /repos/{owner}/{repo}/collaborators/{collaborator} repository repoAddCollaborator | ||||||
| 	// --- | 	// --- | ||||||
| 	// summary: Add a collaborator to a repository | 	// summary: Add or Update a collaborator to a repository | ||||||
| 	// produces: | 	// produces: | ||||||
| 	// - application/json | 	// - application/json | ||||||
| 	// parameters: | 	// parameters: | ||||||
| @@ -177,20 +176,18 @@ func AddCollaborator(ctx *context.APIContext) { | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if err := repo_module.AddCollaborator(ctx, ctx.Repo.Repository, collaborator); err != nil { | 	p := perm.AccessModeWrite | ||||||
| 		if errors.Is(err, user_model.ErrBlockedUser) { | 	if form.Permission != nil { | ||||||
| 			ctx.Error(http.StatusForbidden, "AddCollaborator", err) | 		p = perm.ParseAccessMode(*form.Permission) | ||||||
| 		} else { |  | ||||||
| 			ctx.Error(http.StatusInternalServerError, "AddCollaborator", err) |  | ||||||
| 		} |  | ||||||
| 		return |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if form.Permission != nil { | 	if err := repo_service.AddOrUpdateCollaborator(ctx, ctx.Repo.Repository, collaborator, p); err != nil { | ||||||
| 		if err := repo_model.ChangeCollaborationAccessMode(ctx, ctx.Repo.Repository, collaborator.ID, perm.ParseAccessMode(*form.Permission)); err != nil { | 		if errors.Is(err, user_model.ErrBlockedUser) { | ||||||
| 			ctx.Error(http.StatusInternalServerError, "ChangeCollaborationAccessMode", err) | 			ctx.Error(http.StatusForbidden, "AddOrUpdateCollaborator", err) | ||||||
| 			return | 		} else { | ||||||
|  | 			ctx.Error(http.StatusInternalServerError, "AddOrUpdateCollaborator", err) | ||||||
| 		} | 		} | ||||||
|  | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	ctx.Status(http.StatusNoContent) | 	ctx.Status(http.StatusNoContent) | ||||||
|   | |||||||
| @@ -14,7 +14,6 @@ import ( | |||||||
| 	unit_model "code.gitea.io/gitea/models/unit" | 	unit_model "code.gitea.io/gitea/models/unit" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| 	repo_module "code.gitea.io/gitea/modules/repository" |  | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| 	"code.gitea.io/gitea/services/context" | 	"code.gitea.io/gitea/services/context" | ||||||
| 	"code.gitea.io/gitea/services/mailer" | 	"code.gitea.io/gitea/services/mailer" | ||||||
| @@ -100,12 +99,12 @@ func CollaborationPost(ctx *context.Context) { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if err = repo_module.AddCollaborator(ctx, ctx.Repo.Repository, u); err != nil { | 	if err = repo_service.AddOrUpdateCollaborator(ctx, ctx.Repo.Repository, u, perm.AccessModeWrite); err != nil { | ||||||
| 		if errors.Is(err, user_model.ErrBlockedUser) { | 		if errors.Is(err, user_model.ErrBlockedUser) { | ||||||
| 			ctx.Flash.Error(ctx.Tr("repo.settings.add_collaborator.blocked_user")) | 			ctx.Flash.Error(ctx.Tr("repo.settings.add_collaborator.blocked_user")) | ||||||
| 			ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration") | 			ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration") | ||||||
| 		} else { | 		} else { | ||||||
| 			ctx.ServerError("AddCollaborator", err) | 			ctx.ServerError("AddOrUpdateCollaborator", err) | ||||||
| 		} | 		} | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -13,6 +13,7 @@ import ( | |||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
|  |  | ||||||
|  | 	_ "code.gitea.io/gitea/models" | ||||||
| 	_ "code.gitea.io/gitea/models/actions" | 	_ "code.gitea.io/gitea/models/actions" | ||||||
|  |  | ||||||
| 	"github.com/stretchr/testify/assert" | 	"github.com/stretchr/testify/assert" | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ import ( | |||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
|  |  | ||||||
|  | 	_ "code.gitea.io/gitea/models" | ||||||
| 	_ "code.gitea.io/gitea/models/actions" | 	_ "code.gitea.io/gitea/models/actions" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ import ( | |||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
|  |  | ||||||
|  | 	_ "code.gitea.io/gitea/models" | ||||||
| 	_ "code.gitea.io/gitea/models/actions" | 	_ "code.gitea.io/gitea/models/actions" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -66,7 +66,7 @@ func AdoptRepository(ctx context.Context, doer, u *user_model.User, opts CreateR | |||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if err := repo_module.CreateRepositoryByExample(ctx, doer, u, repo, true, false); err != nil { | 		if err := CreateRepositoryByExample(ctx, doer, u, repo, true, false); err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -9,11 +9,60 @@ import ( | |||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	"code.gitea.io/gitea/models/perm" | ||||||
| 	access_model "code.gitea.io/gitea/models/perm/access" | 	access_model "code.gitea.io/gitea/models/perm/access" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
|  |  | ||||||
|  | 	"xorm.io/builder" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | func AddOrUpdateCollaborator(ctx context.Context, repo *repo_model.Repository, u *user_model.User, mode perm.AccessMode) error { | ||||||
|  | 	// only allow valid access modes, read, write and admin | ||||||
|  | 	if mode < perm.AccessModeRead || mode > perm.AccessModeAdmin { | ||||||
|  | 		return perm.ErrInvalidAccessMode | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err := repo.LoadOwner(ctx); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if user_model.IsUserBlockedBy(ctx, u, repo.OwnerID) || user_model.IsUserBlockedBy(ctx, repo.Owner, u.ID) { | ||||||
|  | 		return user_model.ErrBlockedUser | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return db.WithTx(ctx, func(ctx context.Context) error { | ||||||
|  | 		collaboration, has, err := db.Get[repo_model.Collaboration](ctx, builder.Eq{ | ||||||
|  | 			"repo_id": repo.ID, | ||||||
|  | 			"user_id": u.ID, | ||||||
|  | 		}) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} else if has { | ||||||
|  | 			if collaboration.Mode == mode { | ||||||
|  | 				return nil | ||||||
|  | 			} | ||||||
|  | 			if _, err = db.GetEngine(ctx). | ||||||
|  | 				Where("repo_id=?", repo.ID). | ||||||
|  | 				And("user_id=?", u.ID). | ||||||
|  | 				Cols("mode"). | ||||||
|  | 				Update(&repo_model.Collaboration{ | ||||||
|  | 					Mode: mode, | ||||||
|  | 				}); err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 		} else if err = db.Insert(ctx, &repo_model.Collaboration{ | ||||||
|  | 			RepoID: repo.ID, | ||||||
|  | 			UserID: u.ID, | ||||||
|  | 			Mode:   mode, | ||||||
|  | 		}); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		return access_model.RecalculateUserAccess(ctx, repo, u.ID) | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
| // DeleteCollaboration removes collaboration relation between the user and repository. | // DeleteCollaboration removes collaboration relation between the user and repository. | ||||||
| func DeleteCollaboration(ctx context.Context, repo *repo_model.Repository, collaborator *user_model.User) (err error) { | func DeleteCollaboration(ctx context.Context, repo *repo_model.Repository, collaborator *user_model.User) (err error) { | ||||||
| 	collaboration := &repo_model.Collaboration{ | 	collaboration := &repo_model.Collaboration{ | ||||||
|   | |||||||
| @@ -7,6 +7,7 @@ import ( | |||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	"code.gitea.io/gitea/models/perm" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| @@ -14,6 +15,21 @@ import ( | |||||||
| 	"github.com/stretchr/testify/assert" | 	"github.com/stretchr/testify/assert" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | func TestRepository_AddCollaborator(t *testing.T) { | ||||||
|  | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  |  | ||||||
|  | 	testSuccess := func(repoID, userID int64) { | ||||||
|  | 		repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID}) | ||||||
|  | 		assert.NoError(t, repo.LoadOwner(db.DefaultContext)) | ||||||
|  | 		user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userID}) | ||||||
|  | 		assert.NoError(t, AddOrUpdateCollaborator(db.DefaultContext, repo, user, perm.AccessModeWrite)) | ||||||
|  | 		unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: repoID}, &user_model.User{ID: userID}) | ||||||
|  | 	} | ||||||
|  | 	testSuccess(1, 4) | ||||||
|  | 	testSuccess(1, 4) | ||||||
|  | 	testSuccess(3, 4) | ||||||
|  | } | ||||||
|  |  | ||||||
| func TestRepository_DeleteCollaboration(t *testing.T) { | func TestRepository_DeleteCollaboration(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -12,9 +12,15 @@ import ( | |||||||
| 	"strings" | 	"strings" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
|  | 	"code.gitea.io/gitea/models" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	"code.gitea.io/gitea/models/organization" | ||||||
|  | 	"code.gitea.io/gitea/models/perm" | ||||||
|  | 	access_model "code.gitea.io/gitea/models/perm/access" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
|  | 	"code.gitea.io/gitea/models/unit" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
|  | 	"code.gitea.io/gitea/models/webhook" | ||||||
| 	"code.gitea.io/gitea/modules/git" | 	"code.gitea.io/gitea/modules/git" | ||||||
| 	"code.gitea.io/gitea/modules/gitrepo" | 	"code.gitea.io/gitea/modules/gitrepo" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| @@ -243,7 +249,7 @@ func CreateRepositoryDirectly(ctx context.Context, doer, u *user_model.User, opt | |||||||
| 	var rollbackRepo *repo_model.Repository | 	var rollbackRepo *repo_model.Repository | ||||||
|  |  | ||||||
| 	if err := db.WithTx(ctx, func(ctx context.Context) error { | 	if err := db.WithTx(ctx, func(ctx context.Context) error { | ||||||
| 		if err := repo_module.CreateRepositoryByExample(ctx, doer, u, repo, false, false); err != nil { | 		if err := CreateRepositoryByExample(ctx, doer, u, repo, false, false); err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| @@ -335,3 +341,136 @@ func CreateRepositoryDirectly(ctx context.Context, doer, u *user_model.User, opt | |||||||
|  |  | ||||||
| 	return repo, nil | 	return repo, nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // CreateRepositoryByExample creates a repository for the user/organization. | ||||||
|  | func CreateRepositoryByExample(ctx context.Context, doer, u *user_model.User, repo *repo_model.Repository, overwriteOrAdopt, isFork bool) (err error) { | ||||||
|  | 	if err = repo_model.IsUsableRepoName(repo.Name); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	has, err := repo_model.IsRepositoryModelExist(ctx, u, repo.Name) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return fmt.Errorf("IsRepositoryExist: %w", err) | ||||||
|  | 	} else if has { | ||||||
|  | 		return repo_model.ErrRepoAlreadyExist{ | ||||||
|  | 			Uname: u.Name, | ||||||
|  | 			Name:  repo.Name, | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	repoPath := repo_model.RepoPath(u.Name, repo.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 { | ||||||
|  | 		log.Error("Files already exist in %s and we are not going to adopt or delete.", repoPath) | ||||||
|  | 		return repo_model.ErrRepoFilesAlreadyExist{ | ||||||
|  | 			Uname: u.Name, | ||||||
|  | 			Name:  repo.Name, | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err = db.Insert(ctx, repo); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	if err = repo_model.DeleteRedirect(ctx, u.ID, repo.Name); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// insert units for repo | ||||||
|  | 	defaultUnits := unit.DefaultRepoUnits | ||||||
|  | 	if isFork { | ||||||
|  | 		defaultUnits = unit.DefaultForkRepoUnits | ||||||
|  | 	} | ||||||
|  | 	units := make([]repo_model.RepoUnit, 0, len(defaultUnits)) | ||||||
|  | 	for _, tp := range defaultUnits { | ||||||
|  | 		if tp == unit.TypeIssues { | ||||||
|  | 			units = append(units, repo_model.RepoUnit{ | ||||||
|  | 				RepoID: repo.ID, | ||||||
|  | 				Type:   tp, | ||||||
|  | 				Config: &repo_model.IssuesConfig{ | ||||||
|  | 					EnableTimetracker:                setting.Service.DefaultEnableTimetracking, | ||||||
|  | 					AllowOnlyContributorsToTrackTime: setting.Service.DefaultAllowOnlyContributorsToTrackTime, | ||||||
|  | 					EnableDependencies:               setting.Service.DefaultEnableDependencies, | ||||||
|  | 				}, | ||||||
|  | 			}) | ||||||
|  | 		} else if tp == unit.TypePullRequests { | ||||||
|  | 			units = append(units, repo_model.RepoUnit{ | ||||||
|  | 				RepoID: repo.ID, | ||||||
|  | 				Type:   tp, | ||||||
|  | 				Config: &repo_model.PullRequestsConfig{ | ||||||
|  | 					AllowMerge: true, AllowRebase: true, AllowRebaseMerge: true, AllowSquash: true, AllowFastForwardOnly: true, | ||||||
|  | 					DefaultMergeStyle: repo_model.MergeStyle(setting.Repository.PullRequest.DefaultMergeStyle), | ||||||
|  | 					AllowRebaseUpdate: true, | ||||||
|  | 				}, | ||||||
|  | 			}) | ||||||
|  | 		} else if tp == unit.TypeProjects { | ||||||
|  | 			units = append(units, repo_model.RepoUnit{ | ||||||
|  | 				RepoID: repo.ID, | ||||||
|  | 				Type:   tp, | ||||||
|  | 				Config: &repo_model.ProjectsConfig{ProjectsMode: repo_model.ProjectsModeAll}, | ||||||
|  | 			}) | ||||||
|  | 		} else { | ||||||
|  | 			units = append(units, repo_model.RepoUnit{ | ||||||
|  | 				RepoID: repo.ID, | ||||||
|  | 				Type:   tp, | ||||||
|  | 			}) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err = db.Insert(ctx, units); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Remember visibility preference. | ||||||
|  | 	u.LastRepoVisibility = repo.IsPrivate | ||||||
|  | 	if err = user_model.UpdateUserCols(ctx, u, "last_repo_visibility"); err != nil { | ||||||
|  | 		return fmt.Errorf("UpdateUserCols: %w", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err = user_model.IncrUserRepoNum(ctx, u.ID); err != nil { | ||||||
|  | 		return fmt.Errorf("IncrUserRepoNum: %w", err) | ||||||
|  | 	} | ||||||
|  | 	u.NumRepos++ | ||||||
|  |  | ||||||
|  | 	// Give access to all members in teams with access to all repositories. | ||||||
|  | 	if u.IsOrganization() { | ||||||
|  | 		teams, err := organization.FindOrgTeams(ctx, u.ID) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return fmt.Errorf("FindOrgTeams: %w", err) | ||||||
|  | 		} | ||||||
|  | 		for _, t := range teams { | ||||||
|  | 			if t.IncludesAllRepositories { | ||||||
|  | 				if err := models.AddRepository(ctx, t, repo); err != nil { | ||||||
|  | 					return fmt.Errorf("AddRepository: %w", err) | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if isAdmin, err := access_model.IsUserRepoAdmin(ctx, repo, doer); err != nil { | ||||||
|  | 			return fmt.Errorf("IsUserRepoAdmin: %w", err) | ||||||
|  | 		} else if !isAdmin { | ||||||
|  | 			// Make creator repo admin if it wasn't assigned automatically | ||||||
|  | 			if err = AddOrUpdateCollaborator(ctx, repo, doer, perm.AccessModeAdmin); err != nil { | ||||||
|  | 				return fmt.Errorf("AddCollaborator: %w", err) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} else if err = access_model.RecalculateAccesses(ctx, repo); err != nil { | ||||||
|  | 		// Organization automatically called this in AddRepository method. | ||||||
|  | 		return fmt.Errorf("RecalculateAccesses: %w", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if setting.Service.AutoWatchNewRepos { | ||||||
|  | 		if err = repo_model.WatchRepo(ctx, doer, repo, true); err != nil { | ||||||
|  | 			return fmt.Errorf("WatchRepo: %w", err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err = webhook.CopyDefaultWebhooksToRepo(ctx, repo.ID); err != nil { | ||||||
|  | 		return fmt.Errorf("CopyDefaultWebhooksToRepo: %w", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|   | |||||||
| @@ -134,7 +134,7 @@ func ForkRepository(ctx context.Context, doer, owner *user_model.User, opts Fork | |||||||
| 	}() | 	}() | ||||||
|  |  | ||||||
| 	err = db.WithTx(ctx, func(txCtx context.Context) error { | 	err = db.WithTx(ctx, func(txCtx context.Context) error { | ||||||
| 		if err = repo_module.CreateRepositoryByExample(txCtx, doer, owner, repo, false, true); err != nil { | 		if err = CreateRepositoryByExample(txCtx, doer, owner, repo, false, true); err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -343,7 +343,7 @@ func generateRepository(ctx context.Context, doer, owner *user_model.User, templ | |||||||
| 		ObjectFormatName: templateRepo.ObjectFormatName, | 		ObjectFormatName: templateRepo.ObjectFormatName, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if err = repo_module.CreateRepositoryByExample(ctx, doer, owner, generateRepo, false, false); err != nil { | 	if err = CreateRepositoryByExample(ctx, doer, owner, generateRepo, false, false); err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -20,7 +20,6 @@ import ( | |||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/globallock" | 	"code.gitea.io/gitea/modules/globallock" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| 	repo_module "code.gitea.io/gitea/modules/repository" |  | ||||||
| 	"code.gitea.io/gitea/modules/util" | 	"code.gitea.io/gitea/modules/util" | ||||||
| 	notify_service "code.gitea.io/gitea/services/notify" | 	notify_service "code.gitea.io/gitea/services/notify" | ||||||
| ) | ) | ||||||
| @@ -419,10 +418,7 @@ func StartRepositoryTransfer(ctx context.Context, doer, newOwner *user_model.Use | |||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	if !hasAccess { | 	if !hasAccess { | ||||||
| 		if err := repo_module.AddCollaborator(ctx, repo, newOwner); err != nil { | 		if err := AddOrUpdateCollaborator(ctx, repo, newOwner, perm.AccessModeRead); err != nil { | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 		if err := repo_model.ChangeCollaborationAccessMode(ctx, repo, newOwner.ID, perm.AccessModeRead); err != nil { |  | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								templates/swagger/v1_json.tmpl
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								templates/swagger/v1_json.tmpl
									
									
									
										generated
									
									
									
								
							| @@ -5095,7 +5095,7 @@ | |||||||
|         "tags": [ |         "tags": [ | ||||||
|           "repository" |           "repository" | ||||||
|         ], |         ], | ||||||
|         "summary": "Add a collaborator to a repository", |         "summary": "Add or Update a collaborator to a repository", | ||||||
|         "operationId": "repoAddCollaborator", |         "operationId": "repoAddCollaborator", | ||||||
|         "parameters": [ |         "parameters": [ | ||||||
|           { |           { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user