diff --git a/models/org.go b/models/org.go index 4d7966b132..182d96f784 100644 --- a/models/org.go +++ b/models/org.go @@ -53,6 +53,7 @@ func (org *User) GetOwnerTeam() (*Team, error) { } func (org *User) getTeams(e Engine) error { + org.Teams = nil return e. Where("org_id=?", org.ID). OrderBy("CASE WHEN name LIKE '" + ownerTeamName + "' THEN '' ELSE name END"). diff --git a/models/org_team.go b/models/org_team.go index b6ef9fb34b..07de1566b6 100644 --- a/models/org_team.go +++ b/models/org_team.go @@ -88,6 +88,7 @@ func (t *Team) IsMember(userID int64) bool { } func (t *Team) getRepositories(e Engine) error { + t.Repos = nil return e.Join("INNER", "team_repo", "repository.id = team_repo.repo_id"). Where("team_repo.team_id=?", t.ID). OrderBy("repository.name"). @@ -159,6 +160,24 @@ func (t *Team) addRepository(e Engine, repo *Repository) (err error) { return nil } +// addAllRepositories adds all repositories to the team. +// If the team already has some repositories they will be left unchanged. +func (t *Team) addAllRepositories(e Engine) error { + err := e.Iterate(&Repository{OwnerID: t.OrgID}, func(i int, bean interface{}) error { + repo := bean.(*Repository) + if !t.hasRepository(e, repo.ID) { + if err := t.addRepository(e, repo); err != nil { + return fmt.Errorf("addRepository: %v", err) + } + } + return nil + }) + if err != nil { + return fmt.Errorf("Iterate organization repositories: %v", err) + } + return nil +} + // AddRepository adds new repository to team of organization. func (t *Team) AddRepository(repo *Repository) (err error) { if repo.OwnerID != t.OrgID { @@ -228,6 +247,10 @@ func (t *Team) RemoveRepository(repoID int64) error { return nil } + if t.IsAllRepositories { + return nil + } + repo, err := GetRepositoryByID(repoID) if err != nil { return err @@ -325,6 +348,14 @@ func NewTeam(t *Team) (err error) { } } + // Add all repositories to the team if it has access to all of them. + if t.IsAllRepositories { + err = t.addAllRepositories(sess) + if err != nil { + return fmt.Errorf("addAllRepositories: %v", err) + } + } + // Update organization number of teams. if _, err = sess.Exec("UPDATE `user` SET num_teams=num_teams+1 WHERE id = ?", t.OrgID); err != nil { sess.Rollback() @@ -431,6 +462,14 @@ func UpdateTeam(t *Team, authChanged bool) (err error) { } } + // Add all repositories to the team if it has access to all of them. + if t.IsAllRepositories { + err = t.addAllRepositories(sess) + if err != nil { + return fmt.Errorf("addAllRepositories: %v", err) + } + } + return sess.Commit() } diff --git a/models/org_team_test.go b/models/org_team_test.go index a81f9c0749..d961445a7a 100644 --- a/models/org_team_test.go +++ b/models/org_team_test.go @@ -5,9 +5,12 @@ package models import ( + "fmt" "strings" "testing" + "code.gitea.io/gitea/modules/structs" + "github.com/stretchr/testify/assert" ) @@ -374,3 +377,115 @@ func TestUsersInTeamsCount(t *testing.T) { test([]int64{1, 2, 3, 4, 5}, []int64{2, 5}, 2) test([]int64{1, 2, 3, 4, 5}, []int64{2, 3, 5}, 3) } + +func TestAllRepositoriesTeams(t *testing.T) { + assert.NoError(t, PrepareTestDatabase()) + + // Get an admin user. + user, err := GetUserByID(1) + assert.NoError(t, err, "GetUserByID") + + // Create org. + org := &User{ + Name: "All repo", + IsActive: true, + Type: UserTypeOrganization, + Visibility: structs.VisibleTypePublic, + } + assert.NoError(t, CreateOrganization(org, user), "CreateOrganization") + + // Check Owner team. + ownerTeam, err := org.GetOwnerTeam() + assert.NoError(t, err, "GetOwnerTeam") + assert.True(t, ownerTeam.IsAllRepositories, "Owner team is all repositories") + + // Create repos. + repoIds := make([]int64, 0) + for i := 1; i <= 3; i++ { + r, err := CreateRepository(user, org, CreateRepoOptions{Name: fmt.Sprintf("repo-%d", i)}) + assert.NoError(t, err, "CreateRepository %d", i) + if r != nil { + repoIds = append(repoIds, r.ID) + } + } + + // Create teams and check repo count. + teams := []*Team{ + ownerTeam, + { + OrgID: org.ID, + Name: "team one", + Authorize: AccessModeRead, + IsAllRepositories: true, + }, + { + OrgID: org.ID, + Name: "team 2", + Authorize: AccessModeRead, + IsAllRepositories: false, + }, + { + OrgID: org.ID, + Name: "team three", + Authorize: AccessModeWrite, + IsAllRepositories: true, + }, + { + OrgID: org.ID, + Name: "team 4", + Authorize: AccessModeWrite, + IsAllRepositories: false, + }, + } + repoCounts := []int{3, 3, 0, 3, 0} + for i, team := range teams { + if i > 0 { // first team is Owner. + assert.NoError(t, NewTeam(team), "team %d: NewTeam", i) + } + assert.NoError(t, team.GetRepositories(), "team %d: GetRepositories", i) + assert.Equal(t, repoCounts[i], len(team.Repos), "team %d: repo count", i) + } + + // Update teams and check repo count. + teams[3].IsAllRepositories = false + teams[4].IsAllRepositories = true + repoCounts[4] = 3 + for i, team := range teams { + assert.NoError(t, UpdateTeam(team, false), "team %d: UpdateTeam", i) + assert.NoError(t, team.GetRepositories(), "team %d: GetRepositories", i) + assert.Equal(t, repoCounts[i], len(team.Repos), "team %d: repo count", i) + } + + // Create repo and check teams repo count. + r, err := CreateRepository(user, org, CreateRepoOptions{Name: "repo-last"}) + assert.NoError(t, err, "CreateRepository last") + if r != nil { + repoIds = append(repoIds, r.ID) + } + repoCounts[0] = 4 + repoCounts[1] = 4 + repoCounts[4] = 4 + for i, team := range teams { + assert.NoError(t, team.GetRepositories(), "team %d: GetRepositories", i) + assert.Equal(t, repoCounts[i], len(team.Repos), "team %d: repo count", i) + } + + // Remove repo and check teams repo count. + assert.NoError(t, DeleteRepository(user, org.ID, repoIds[0]), "DeleteRepository") + repoCounts[0] = 3 + repoCounts[1] = 3 + repoCounts[3] = 2 + repoCounts[4] = 3 + for i, team := range teams { + assert.NoError(t, team.GetRepositories(), "team %d: GetRepositories", i) + assert.Equal(t, repoCounts[i], len(team.Repos), "team %d: repo count", i) + } + + // Wipe created items. + for i, rid := range repoIds { + if i > 0 { // first repo already deleted. + assert.NoError(t, DeleteRepository(user, org.ID, rid), "DeleteRepository %d", i) + } + } + assert.NoError(t, DeleteOrganization(org), "DeleteOrganization") +} diff --git a/models/repo.go b/models/repo.go index 936ad2ae37..9bf1430347 100644 --- a/models/repo.go +++ b/models/repo.go @@ -1355,14 +1355,19 @@ func createRepository(e *xorm.Session, doer, u *User, repo *Repository) (err err return fmt.Errorf("updateUser: %v", err) } - // Give access to all members in owner team. + // Give access to all members in teams with access to all repositories. if u.IsOrganization() { - t, err := u.getOwnerTeam(e) - if err != nil { - return fmt.Errorf("getOwnerTeam: %v", err) - } else if err = t.addRepository(e, repo); err != nil { - return fmt.Errorf("addRepository: %v", err) - } else if err = prepareWebhooks(e, repo, HookEventRepository, &api.RepositoryPayload{ + if err := u.GetTeams(); err != nil { + return fmt.Errorf("GetTeams: %v", err) + } + for _, t := range u.Teams { + if t.IsAllRepositories { + if err := t.addRepository(e, repo); err != nil { + return fmt.Errorf("addRepository: %v", err) + } + } + } + if err := prepareWebhooks(e, repo, HookEventRepository, &api.RepositoryPayload{ Action: api.HookRepoCreated, Repository: repo.innerAPIFormat(e, AccessModeOwner, false), Organization: u.APIFormat(),