mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-26 12:27:06 +00:00 
			
		
		
		
	Add team option to grant rights for all organization repositories (#8688)
* Add field IsAllRepositories to team
* Add AllRepositories to team UI
* Manage team with access to all repositories
* Add field IsAllRepositories to team API
* put backticks around table/column names
* rename IsAllRepositories to IncludesAllRepositories
* do not reload slice if already loaded
* add repo to teams with access to all repositories when changing repo owner
* improve tests for teams with access to all repositories
* Merge branch 'master'
* Change code for adding all repositories
Signed-off-by: David Svantesson <davidsvantesson@gmail.com>
* fmt after merge
* Change code in API EditTeam similar to EditTeamPost web interface
Signed-off-by: David Svantesson <davidsvantesson@gmail.com>
* Clarify that all repositories will be added
Signed-off-by: David Svantesson <davidsvantesson@gmail.com>
* All repositories option under Permissions headline
* New setting group 'Repository access'
* Move check IncludeAllRepositories to removeRepository.
* Revert "Move check IncludeAllRepositories to removeRepository." and add comment instead.
This reverts commit 753b7d205b.
* Clarify help text what options do.
			
			
This commit is contained in:
		 David Svantesson
					David Svantesson
				
			
				
					committed by
					
						 Lauris BH
						Lauris BH
					
				
			
			
				
	
			
			
			 Lauris BH
						Lauris BH
					
				
			
						parent
						
							0109229928
						
					
				
				
					commit
					72aa5a20ec
				
			| @@ -55,37 +55,44 @@ func TestAPITeam(t *testing.T) { | |||||||
|  |  | ||||||
| 	// Create team. | 	// Create team. | ||||||
| 	teamToCreate := &api.CreateTeamOption{ | 	teamToCreate := &api.CreateTeamOption{ | ||||||
| 		Name:        "team1", | 		Name:                    "team1", | ||||||
| 		Description: "team one", | 		Description:             "team one", | ||||||
| 		Permission:  "write", | 		IncludesAllRepositories: true, | ||||||
| 		Units:       []string{"repo.code", "repo.issues"}, | 		Permission:              "write", | ||||||
|  | 		Units:                   []string{"repo.code", "repo.issues"}, | ||||||
| 	} | 	} | ||||||
| 	req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/orgs/%s/teams?token=%s", org.Name, token), teamToCreate) | 	req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/orgs/%s/teams?token=%s", org.Name, token), teamToCreate) | ||||||
| 	resp = session.MakeRequest(t, req, http.StatusCreated) | 	resp = session.MakeRequest(t, req, http.StatusCreated) | ||||||
| 	DecodeJSON(t, resp, &apiTeam) | 	DecodeJSON(t, resp, &apiTeam) | ||||||
| 	checkTeamResponse(t, &apiTeam, teamToCreate.Name, teamToCreate.Description, teamToCreate.Permission, teamToCreate.Units) | 	checkTeamResponse(t, &apiTeam, teamToCreate.Name, teamToCreate.Description, teamToCreate.IncludesAllRepositories, | ||||||
| 	checkTeamBean(t, apiTeam.ID, teamToCreate.Name, teamToCreate.Description, teamToCreate.Permission, teamToCreate.Units) | 		teamToCreate.Permission, teamToCreate.Units) | ||||||
|  | 	checkTeamBean(t, apiTeam.ID, teamToCreate.Name, teamToCreate.Description, teamToCreate.IncludesAllRepositories, | ||||||
|  | 		teamToCreate.Permission, teamToCreate.Units) | ||||||
| 	teamID := apiTeam.ID | 	teamID := apiTeam.ID | ||||||
|  |  | ||||||
| 	// Edit team. | 	// Edit team. | ||||||
| 	teamToEdit := &api.EditTeamOption{ | 	teamToEdit := &api.EditTeamOption{ | ||||||
| 		Name:        "teamone", | 		Name:                    "teamone", | ||||||
| 		Description: "team 1", | 		Description:             "team 1", | ||||||
| 		Permission:  "admin", | 		IncludesAllRepositories: false, | ||||||
| 		Units:       []string{"repo.code", "repo.pulls", "repo.releases"}, | 		Permission:              "admin", | ||||||
|  | 		Units:                   []string{"repo.code", "repo.pulls", "repo.releases"}, | ||||||
| 	} | 	} | ||||||
| 	req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/teams/%d?token=%s", teamID, token), teamToEdit) | 	req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/teams/%d?token=%s", teamID, token), teamToEdit) | ||||||
| 	resp = session.MakeRequest(t, req, http.StatusOK) | 	resp = session.MakeRequest(t, req, http.StatusOK) | ||||||
| 	DecodeJSON(t, resp, &apiTeam) | 	DecodeJSON(t, resp, &apiTeam) | ||||||
| 	checkTeamResponse(t, &apiTeam, teamToEdit.Name, teamToEdit.Description, teamToEdit.Permission, teamToEdit.Units) | 	checkTeamResponse(t, &apiTeam, teamToEdit.Name, teamToEdit.Description, teamToEdit.IncludesAllRepositories, | ||||||
| 	checkTeamBean(t, apiTeam.ID, teamToEdit.Name, teamToEdit.Description, teamToEdit.Permission, teamToEdit.Units) | 		teamToEdit.Permission, teamToEdit.Units) | ||||||
|  | 	checkTeamBean(t, apiTeam.ID, teamToEdit.Name, teamToEdit.Description, teamToEdit.IncludesAllRepositories, | ||||||
|  | 		teamToEdit.Permission, teamToEdit.Units) | ||||||
|  |  | ||||||
| 	// Read team. | 	// Read team. | ||||||
| 	teamRead := models.AssertExistsAndLoadBean(t, &models.Team{ID: teamID}).(*models.Team) | 	teamRead := models.AssertExistsAndLoadBean(t, &models.Team{ID: teamID}).(*models.Team) | ||||||
| 	req = NewRequestf(t, "GET", "/api/v1/teams/%d?token="+token, teamID) | 	req = NewRequestf(t, "GET", "/api/v1/teams/%d?token="+token, teamID) | ||||||
| 	resp = session.MakeRequest(t, req, http.StatusOK) | 	resp = session.MakeRequest(t, req, http.StatusOK) | ||||||
| 	DecodeJSON(t, resp, &apiTeam) | 	DecodeJSON(t, resp, &apiTeam) | ||||||
| 	checkTeamResponse(t, &apiTeam, teamRead.Name, teamRead.Description, teamRead.Authorize.String(), teamRead.GetUnitNames()) | 	checkTeamResponse(t, &apiTeam, teamRead.Name, teamRead.Description, teamRead.IncludesAllRepositories, | ||||||
|  | 		teamRead.Authorize.String(), teamRead.GetUnitNames()) | ||||||
|  |  | ||||||
| 	// Delete team. | 	// Delete team. | ||||||
| 	req = NewRequestf(t, "DELETE", "/api/v1/teams/%d?token="+token, teamID) | 	req = NewRequestf(t, "DELETE", "/api/v1/teams/%d?token="+token, teamID) | ||||||
| @@ -93,19 +100,20 @@ func TestAPITeam(t *testing.T) { | |||||||
| 	models.AssertNotExistsBean(t, &models.Team{ID: teamID}) | 	models.AssertNotExistsBean(t, &models.Team{ID: teamID}) | ||||||
| } | } | ||||||
|  |  | ||||||
| func checkTeamResponse(t *testing.T, apiTeam *api.Team, name, description string, permission string, units []string) { | func checkTeamResponse(t *testing.T, apiTeam *api.Team, name, description string, includesAllRepositories bool, permission string, units []string) { | ||||||
| 	assert.Equal(t, name, apiTeam.Name, "name") | 	assert.Equal(t, name, apiTeam.Name, "name") | ||||||
| 	assert.Equal(t, description, apiTeam.Description, "description") | 	assert.Equal(t, description, apiTeam.Description, "description") | ||||||
|  | 	assert.Equal(t, includesAllRepositories, apiTeam.IncludesAllRepositories, "includesAllRepositories") | ||||||
| 	assert.Equal(t, permission, apiTeam.Permission, "permission") | 	assert.Equal(t, permission, apiTeam.Permission, "permission") | ||||||
| 	sort.StringSlice(units).Sort() | 	sort.StringSlice(units).Sort() | ||||||
| 	sort.StringSlice(apiTeam.Units).Sort() | 	sort.StringSlice(apiTeam.Units).Sort() | ||||||
| 	assert.EqualValues(t, units, apiTeam.Units, "units") | 	assert.EqualValues(t, units, apiTeam.Units, "units") | ||||||
| } | } | ||||||
|  |  | ||||||
| func checkTeamBean(t *testing.T, id int64, name, description string, permission string, units []string) { | func checkTeamBean(t *testing.T, id int64, name, description string, includesAllRepositories bool, permission string, units []string) { | ||||||
| 	team := models.AssertExistsAndLoadBean(t, &models.Team{ID: id}).(*models.Team) | 	team := models.AssertExistsAndLoadBean(t, &models.Team{ID: id}).(*models.Team) | ||||||
| 	assert.NoError(t, team.GetUnits(), "GetUnits") | 	assert.NoError(t, team.GetUnits(), "GetUnits") | ||||||
| 	checkTeamResponse(t, convert.ToTeam(team), name, description, permission, units) | 	checkTeamResponse(t, convert.ToTeam(team), name, description, includesAllRepositories, permission, units) | ||||||
| } | } | ||||||
|  |  | ||||||
| type TeamSearchResults struct { | type TeamSearchResults struct { | ||||||
|   | |||||||
| @@ -264,6 +264,8 @@ var migrations = []Migration{ | |||||||
| 	NewMigration("Add WhitelistDeployKeys to protected branch", addWhitelistDeployKeysToBranches), | 	NewMigration("Add WhitelistDeployKeys to protected branch", addWhitelistDeployKeysToBranches), | ||||||
| 	// v104 -> v105 | 	// v104 -> v105 | ||||||
| 	NewMigration("remove unnecessary columns from label", removeLabelUneededCols), | 	NewMigration("remove unnecessary columns from label", removeLabelUneededCols), | ||||||
|  | 	// v105 -> v106 | ||||||
|  | 	NewMigration("add includes_all_repositories to teams", addTeamIncludesAllRepositories), | ||||||
| } | } | ||||||
|  |  | ||||||
| // Migrate database to current version | // Migrate database to current version | ||||||
|   | |||||||
							
								
								
									
										25
									
								
								models/migrations/v105.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								models/migrations/v105.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | |||||||
|  | // Copyright 2019 The Gitea Authors. All rights reserved. | ||||||
|  | // Use of this source code is governed by a MIT-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package migrations | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"xorm.io/xorm" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func addTeamIncludesAllRepositories(x *xorm.Engine) error { | ||||||
|  |  | ||||||
|  | 	type Team struct { | ||||||
|  | 		ID                      int64 `xorm:"pk autoincr"` | ||||||
|  | 		IncludesAllRepositories bool  `xorm:"NOT NULL DEFAULT false"` | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err := x.Sync2(new(Team)); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	_, err := x.Exec("UPDATE `team` SET `includes_all_repositories` = ? WHERE `name`=?", | ||||||
|  | 		true, "Owners") | ||||||
|  | 	return err | ||||||
|  | } | ||||||
| @@ -48,6 +48,9 @@ func (org *User) GetOwnerTeam() (*Team, error) { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (org *User) getTeams(e Engine) error { | func (org *User) getTeams(e Engine) error { | ||||||
|  | 	if org.Teams != nil { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
| 	return e. | 	return e. | ||||||
| 		Where("org_id=?", org.ID). | 		Where("org_id=?", org.ID). | ||||||
| 		OrderBy("CASE WHEN name LIKE '" + ownerTeamName + "' THEN '' ELSE name END"). | 		OrderBy("CASE WHEN name LIKE '" + ownerTeamName + "' THEN '' ELSE name END"). | ||||||
| @@ -149,11 +152,12 @@ func CreateOrganization(org, owner *User) (err error) { | |||||||
|  |  | ||||||
| 	// Create default owner team. | 	// Create default owner team. | ||||||
| 	t := &Team{ | 	t := &Team{ | ||||||
| 		OrgID:      org.ID, | 		OrgID:                   org.ID, | ||||||
| 		LowerName:  strings.ToLower(ownerTeamName), | 		LowerName:               strings.ToLower(ownerTeamName), | ||||||
| 		Name:       ownerTeamName, | 		Name:                    ownerTeamName, | ||||||
| 		Authorize:  AccessModeOwner, | 		Authorize:               AccessModeOwner, | ||||||
| 		NumMembers: 1, | 		NumMembers:              1, | ||||||
|  | 		IncludesAllRepositories: true, | ||||||
| 	} | 	} | ||||||
| 	if _, err = sess.Insert(t); err != nil { | 	if _, err = sess.Insert(t); err != nil { | ||||||
| 		return fmt.Errorf("insert owner team: %v", err) | 		return fmt.Errorf("insert owner team: %v", err) | ||||||
|   | |||||||
| @@ -22,17 +22,18 @@ const ownerTeamName = "Owners" | |||||||
|  |  | ||||||
| // Team represents a organization team. | // Team represents a organization team. | ||||||
| type Team struct { | type Team struct { | ||||||
| 	ID          int64 `xorm:"pk autoincr"` | 	ID                      int64 `xorm:"pk autoincr"` | ||||||
| 	OrgID       int64 `xorm:"INDEX"` | 	OrgID                   int64 `xorm:"INDEX"` | ||||||
| 	LowerName   string | 	LowerName               string | ||||||
| 	Name        string | 	Name                    string | ||||||
| 	Description string | 	Description             string | ||||||
| 	Authorize   AccessMode | 	Authorize               AccessMode | ||||||
| 	Repos       []*Repository `xorm:"-"` | 	Repos                   []*Repository `xorm:"-"` | ||||||
| 	Members     []*User       `xorm:"-"` | 	Members                 []*User       `xorm:"-"` | ||||||
| 	NumRepos    int | 	NumRepos                int | ||||||
| 	NumMembers  int | 	NumMembers              int | ||||||
| 	Units       []*TeamUnit `xorm:"-"` | 	Units                   []*TeamUnit `xorm:"-"` | ||||||
|  | 	IncludesAllRepositories bool        `xorm:"NOT NULL DEFAULT false"` | ||||||
| } | } | ||||||
|  |  | ||||||
| // SearchTeamOptions holds the search options | // SearchTeamOptions holds the search options | ||||||
| @@ -149,6 +150,9 @@ func (t *Team) IsMember(userID int64) bool { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (t *Team) getRepositories(e Engine) error { | func (t *Team) getRepositories(e Engine) error { | ||||||
|  | 	if t.Repos != nil { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
| 	return e.Join("INNER", "team_repo", "repository.id = team_repo.repo_id"). | 	return e.Join("INNER", "team_repo", "repository.id = team_repo.repo_id"). | ||||||
| 		Where("team_repo.team_id=?", t.ID). | 		Where("team_repo.team_id=?", t.ID). | ||||||
| 		OrderBy("repository.name"). | 		OrderBy("repository.name"). | ||||||
| @@ -220,6 +224,25 @@ func (t *Team) addRepository(e Engine, repo *Repository) (err error) { | |||||||
| 	return nil | 	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 { | ||||||
|  | 	var orgRepos []Repository | ||||||
|  | 	if err := e.Where("owner_id = ?", t.OrgID).Find(&orgRepos); err != nil { | ||||||
|  | 		return fmt.Errorf("get org repos: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, repo := range orgRepos { | ||||||
|  | 		if !t.hasRepository(e, repo.ID) { | ||||||
|  | 			if err := t.addRepository(e, &repo); err != nil { | ||||||
|  | 				return fmt.Errorf("addRepository: %v", err) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
| // AddRepository adds new repository to team of organization. | // AddRepository adds new repository to team of organization. | ||||||
| func (t *Team) AddRepository(repo *Repository) (err error) { | func (t *Team) AddRepository(repo *Repository) (err error) { | ||||||
| 	if repo.OwnerID != t.OrgID { | 	if repo.OwnerID != t.OrgID { | ||||||
| @@ -241,6 +264,8 @@ func (t *Team) AddRepository(repo *Repository) (err error) { | |||||||
| 	return sess.Commit() | 	return sess.Commit() | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // removeRepository removes a repository from a team and recalculates access | ||||||
|  | // Note: Repository shall not be removed from team if it includes all repositories (unless the repository is deleted) | ||||||
| func (t *Team) removeRepository(e Engine, repo *Repository, recalculate bool) (err error) { | func (t *Team) removeRepository(e Engine, repo *Repository, recalculate bool) (err error) { | ||||||
| 	if err = removeTeamRepo(e, t.ID, repo.ID); err != nil { | 	if err = removeTeamRepo(e, t.ID, repo.ID); err != nil { | ||||||
| 		return err | 		return err | ||||||
| @@ -284,11 +309,16 @@ func (t *Team) removeRepository(e Engine, repo *Repository, recalculate bool) (e | |||||||
| } | } | ||||||
|  |  | ||||||
| // RemoveRepository removes repository from team of organization. | // RemoveRepository removes repository from team of organization. | ||||||
|  | // If the team shall include all repositories the request is ignored. | ||||||
| func (t *Team) RemoveRepository(repoID int64) error { | func (t *Team) RemoveRepository(repoID int64) error { | ||||||
| 	if !t.HasRepository(repoID) { | 	if !t.HasRepository(repoID) { | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	if t.IncludesAllRepositories { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	repo, err := GetRepositoryByID(repoID) | 	repo, err := GetRepositoryByID(repoID) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| @@ -394,6 +424,14 @@ func NewTeam(t *Team) (err error) { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	// Add all repositories to the team if it has access to all of them. | ||||||
|  | 	if t.IncludesAllRepositories { | ||||||
|  | 		err = t.addAllRepositories(sess) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return fmt.Errorf("addAllRepositories: %v", err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	// Update organization number of teams. | 	// Update organization number of teams. | ||||||
| 	if _, err = sess.Exec("UPDATE `user` SET num_teams=num_teams+1 WHERE id = ?", t.OrgID); err != nil { | 	if _, err = sess.Exec("UPDATE `user` SET num_teams=num_teams+1 WHERE id = ?", t.OrgID); err != nil { | ||||||
| 		errRollback := sess.Rollback() | 		errRollback := sess.Rollback() | ||||||
| @@ -446,7 +484,7 @@ func GetTeamByID(teamID int64) (*Team, error) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // UpdateTeam updates information of team. | // UpdateTeam updates information of team. | ||||||
| func UpdateTeam(t *Team, authChanged bool) (err error) { | func UpdateTeam(t *Team, authChanged bool, includeAllChanged bool) (err error) { | ||||||
| 	if len(t.Name) == 0 { | 	if len(t.Name) == 0 { | ||||||
| 		return errors.New("empty team name") | 		return errors.New("empty team name") | ||||||
| 	} | 	} | ||||||
| @@ -511,6 +549,14 @@ func UpdateTeam(t *Team, authChanged bool) (err error) { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	// Add all repositories to the team if it has access to all of them. | ||||||
|  | 	if includeAllChanged && t.IncludesAllRepositories { | ||||||
|  | 		err = t.addAllRepositories(sess) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return fmt.Errorf("addAllRepositories: %v", err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	return sess.Commit() | 	return sess.Commit() | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -5,9 +5,12 @@ | |||||||
| package models | package models | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"fmt" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	"code.gitea.io/gitea/modules/structs" | ||||||
|  |  | ||||||
| 	"github.com/stretchr/testify/assert" | 	"github.com/stretchr/testify/assert" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -206,7 +209,7 @@ func TestUpdateTeam(t *testing.T) { | |||||||
| 	team.Name = "newName" | 	team.Name = "newName" | ||||||
| 	team.Description = strings.Repeat("A long description!", 100) | 	team.Description = strings.Repeat("A long description!", 100) | ||||||
| 	team.Authorize = AccessModeAdmin | 	team.Authorize = AccessModeAdmin | ||||||
| 	assert.NoError(t, UpdateTeam(team, true)) | 	assert.NoError(t, UpdateTeam(team, true, false)) | ||||||
|  |  | ||||||
| 	team = AssertExistsAndLoadBean(t, &Team{Name: "newName"}).(*Team) | 	team = AssertExistsAndLoadBean(t, &Team{Name: "newName"}).(*Team) | ||||||
| 	assert.True(t, strings.HasPrefix(team.Description, "A long description!")) | 	assert.True(t, strings.HasPrefix(team.Description, "A long description!")) | ||||||
| @@ -225,7 +228,7 @@ func TestUpdateTeam2(t *testing.T) { | |||||||
| 	team.LowerName = "owners" | 	team.LowerName = "owners" | ||||||
| 	team.Name = "Owners" | 	team.Name = "Owners" | ||||||
| 	team.Description = strings.Repeat("A long description!", 100) | 	team.Description = strings.Repeat("A long description!", 100) | ||||||
| 	err := UpdateTeam(team, true) | 	err := UpdateTeam(team, true, false) | ||||||
| 	assert.True(t, IsErrTeamAlreadyExist(err)) | 	assert.True(t, IsErrTeamAlreadyExist(err)) | ||||||
|  |  | ||||||
| 	CheckConsistencyFor(t, &Team{ID: team.ID}) | 	CheckConsistencyFor(t, &Team{ID: team.ID}) | ||||||
| @@ -374,3 +377,133 @@ func TestUsersInTeamsCount(t *testing.T) { | |||||||
| 	test([]int64{1, 2, 3, 4, 5}, []int64{2, 5}, 2)    // userid 2,4 | 	test([]int64{1, 2, 3, 4, 5}, []int64{2, 5}, 2)    // userid 2,4 | ||||||
| 	test([]int64{1, 2, 3, 4, 5}, []int64{2, 3, 5}, 3) // userid 2,4,5 | 	test([]int64{1, 2, 3, 4, 5}, []int64{2, 3, 5}, 3) // userid 2,4,5 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func TestIncludesAllRepositoriesTeams(t *testing.T) { | ||||||
|  | 	assert.NoError(t, PrepareTestDatabase()) | ||||||
|  |  | ||||||
|  | 	testTeamRepositories := func(teamID int64, repoIds []int64) { | ||||||
|  | 		team := AssertExistsAndLoadBean(t, &Team{ID: teamID}).(*Team) | ||||||
|  | 		assert.NoError(t, team.GetRepositories(), "%s: GetRepositories", team.Name) | ||||||
|  | 		assert.Len(t, team.Repos, team.NumRepos, "%s: len repo", team.Name) | ||||||
|  | 		assert.Equal(t, len(repoIds), len(team.Repos), "%s: repo count", team.Name) | ||||||
|  | 		for i, rid := range repoIds { | ||||||
|  | 			if rid > 0 { | ||||||
|  | 				assert.True(t, team.HasRepository(rid), "%s: HasRepository(%d) %d", rid, i) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 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.IncludesAllRepositories, "Owner team includes all repositories") | ||||||
|  |  | ||||||
|  | 	// Create repos. | ||||||
|  | 	repoIds := make([]int64, 0) | ||||||
|  | 	for i := 0; 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) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	// Get fresh copy of Owner team after creating repos. | ||||||
|  | 	ownerTeam, err = org.GetOwnerTeam() | ||||||
|  | 	assert.NoError(t, err, "GetOwnerTeam") | ||||||
|  |  | ||||||
|  | 	// Create teams and check repositories. | ||||||
|  | 	teams := []*Team{ | ||||||
|  | 		ownerTeam, | ||||||
|  | 		{ | ||||||
|  | 			OrgID:                   org.ID, | ||||||
|  | 			Name:                    "team one", | ||||||
|  | 			Authorize:               AccessModeRead, | ||||||
|  | 			IncludesAllRepositories: true, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			OrgID:                   org.ID, | ||||||
|  | 			Name:                    "team 2", | ||||||
|  | 			Authorize:               AccessModeRead, | ||||||
|  | 			IncludesAllRepositories: false, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			OrgID:                   org.ID, | ||||||
|  | 			Name:                    "team three", | ||||||
|  | 			Authorize:               AccessModeWrite, | ||||||
|  | 			IncludesAllRepositories: true, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			OrgID:                   org.ID, | ||||||
|  | 			Name:                    "team 4", | ||||||
|  | 			Authorize:               AccessModeWrite, | ||||||
|  | 			IncludesAllRepositories: false, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	teamRepos := [][]int64{ | ||||||
|  | 		repoIds, | ||||||
|  | 		repoIds, | ||||||
|  | 		{}, | ||||||
|  | 		repoIds, | ||||||
|  | 		{}, | ||||||
|  | 	} | ||||||
|  | 	for i, team := range teams { | ||||||
|  | 		if i > 0 { // first team is Owner. | ||||||
|  | 			assert.NoError(t, NewTeam(team), "%s: NewTeam", team.Name) | ||||||
|  | 		} | ||||||
|  | 		testTeamRepositories(team.ID, teamRepos[i]) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Update teams and check repositories. | ||||||
|  | 	teams[3].IncludesAllRepositories = false | ||||||
|  | 	teams[4].IncludesAllRepositories = true | ||||||
|  | 	teamRepos[4] = repoIds | ||||||
|  | 	for i, team := range teams { | ||||||
|  | 		assert.NoError(t, UpdateTeam(team, false, true), "%s: UpdateTeam", team.Name) | ||||||
|  | 		testTeamRepositories(team.ID, teamRepos[i]) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Create repo and check teams repositories. | ||||||
|  | 	org.Teams = nil // Reset teams to allow their reloading. | ||||||
|  | 	r, err := CreateRepository(user, org, CreateRepoOptions{Name: "repo-last"}) | ||||||
|  | 	assert.NoError(t, err, "CreateRepository last") | ||||||
|  | 	if r != nil { | ||||||
|  | 		repoIds = append(repoIds, r.ID) | ||||||
|  | 	} | ||||||
|  | 	teamRepos[0] = repoIds | ||||||
|  | 	teamRepos[1] = repoIds | ||||||
|  | 	teamRepos[4] = repoIds | ||||||
|  | 	for i, team := range teams { | ||||||
|  | 		testTeamRepositories(team.ID, teamRepos[i]) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Remove repo and check teams repositories. | ||||||
|  | 	assert.NoError(t, DeleteRepository(user, org.ID, repoIds[0]), "DeleteRepository") | ||||||
|  | 	teamRepos[0] = repoIds[1:] | ||||||
|  | 	teamRepos[1] = repoIds[1:] | ||||||
|  | 	teamRepos[3] = repoIds[1:3] | ||||||
|  | 	teamRepos[4] = repoIds[1:] | ||||||
|  | 	for i, team := range teams { | ||||||
|  | 		testTeamRepositories(team.ID, teamRepos[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") | ||||||
|  | } | ||||||
|   | |||||||
| @@ -1447,14 +1447,17 @@ func createRepository(e *xorm.Session, doer, u *User, repo *Repository) (err err | |||||||
| 	} | 	} | ||||||
| 	u.NumRepos++ | 	u.NumRepos++ | ||||||
|  |  | ||||||
| 	// Give access to all members in owner team. | 	// Give access to all members in teams with access to all repositories. | ||||||
| 	if u.IsOrganization() { | 	if u.IsOrganization() { | ||||||
| 		t, err := u.getOwnerTeam(e) | 		if err := u.GetTeams(); err != nil { | ||||||
| 		if err != nil { | 			return fmt.Errorf("GetTeams: %v", err) | ||||||
| 			return fmt.Errorf("getOwnerTeam: %v", err) |  | ||||||
| 		} | 		} | ||||||
| 		if err = t.addRepository(e, repo); err != nil { | 		for _, t := range u.Teams { | ||||||
| 			return fmt.Errorf("addRepository: %v", err) | 			if t.IncludesAllRepositories { | ||||||
|  | 				if err := t.addRepository(e, repo); err != nil { | ||||||
|  | 					return fmt.Errorf("addRepository: %v", err) | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 	} else if err = repo.recalculateAccesses(e); err != nil { | 	} else if err = repo.recalculateAccesses(e); err != nil { | ||||||
| 		// Organization automatically called this in addRepository method. | 		// Organization automatically called this in addRepository method. | ||||||
| @@ -1641,11 +1644,15 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if newOwner.IsOrganization() { | 	if newOwner.IsOrganization() { | ||||||
| 		t, err := newOwner.getOwnerTeam(sess) | 		if err := newOwner.GetTeams(); err != nil { | ||||||
| 		if err != nil { | 			return fmt.Errorf("GetTeams: %v", err) | ||||||
| 			return fmt.Errorf("getOwnerTeam: %v", err) | 		} | ||||||
| 		} else if err = t.addRepository(sess, repo); err != nil { | 		for _, t := range newOwner.Teams { | ||||||
| 			return fmt.Errorf("add to owner team: %v", err) | 			if t.IncludesAllRepositories { | ||||||
|  | 				if err := t.addRepository(sess, repo); err != nil { | ||||||
|  | 					return fmt.Errorf("addRepository: %v", err) | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 	} else if err = repo.recalculateAccesses(sess); err != nil { | 	} else if err = repo.recalculateAccesses(sess); err != nil { | ||||||
| 		// Organization called this in addRepository method. | 		// Organization called this in addRepository method. | ||||||
|   | |||||||
| @@ -62,6 +62,7 @@ type CreateTeamForm struct { | |||||||
| 	Description string `binding:"MaxSize(255)"` | 	Description string `binding:"MaxSize(255)"` | ||||||
| 	Permission  string | 	Permission  string | ||||||
| 	Units       []models.UnitType | 	Units       []models.UnitType | ||||||
|  | 	RepoAccess  string | ||||||
| } | } | ||||||
|  |  | ||||||
| // Validate validates the fields | // Validate validates the fields | ||||||
|   | |||||||
| @@ -7,10 +7,11 @@ package structs | |||||||
|  |  | ||||||
| // Team represents a team in an organization | // Team represents a team in an organization | ||||||
| type Team struct { | type Team struct { | ||||||
| 	ID           int64         `json:"id"` | 	ID                      int64         `json:"id"` | ||||||
| 	Name         string        `json:"name"` | 	Name                    string        `json:"name"` | ||||||
| 	Description  string        `json:"description"` | 	Description             string        `json:"description"` | ||||||
| 	Organization *Organization `json:"organization"` | 	Organization            *Organization `json:"organization"` | ||||||
|  | 	IncludesAllRepositories bool          `json:"includes_all_repositories"` | ||||||
| 	// enum: none,read,write,admin,owner | 	// enum: none,read,write,admin,owner | ||||||
| 	Permission string `json:"permission"` | 	Permission string `json:"permission"` | ||||||
| 	// example: ["repo.code","repo.issues","repo.ext_issues","repo.wiki","repo.pulls","repo.releases","repo.ext_wiki"] | 	// example: ["repo.code","repo.issues","repo.ext_issues","repo.wiki","repo.pulls","repo.releases","repo.ext_wiki"] | ||||||
| @@ -20,8 +21,9 @@ type Team struct { | |||||||
| // CreateTeamOption options for creating a team | // CreateTeamOption options for creating a team | ||||||
| type CreateTeamOption struct { | type CreateTeamOption struct { | ||||||
| 	// required: true | 	// required: true | ||||||
| 	Name        string `json:"name" binding:"Required;AlphaDashDot;MaxSize(30)"` | 	Name                    string `json:"name" binding:"Required;AlphaDashDot;MaxSize(30)"` | ||||||
| 	Description string `json:"description" binding:"MaxSize(255)"` | 	Description             string `json:"description" binding:"MaxSize(255)"` | ||||||
|  | 	IncludesAllRepositories bool   `json:"includes_all_repositories"` | ||||||
| 	// enum: read,write,admin | 	// enum: read,write,admin | ||||||
| 	Permission string `json:"permission"` | 	Permission string `json:"permission"` | ||||||
| 	// example: ["repo.code","repo.issues","repo.ext_issues","repo.wiki","repo.pulls","repo.releases","repo.ext_wiki"] | 	// example: ["repo.code","repo.issues","repo.ext_issues","repo.wiki","repo.pulls","repo.releases","repo.ext_wiki"] | ||||||
| @@ -31,8 +33,9 @@ type CreateTeamOption struct { | |||||||
| // EditTeamOption options for editing a team | // EditTeamOption options for editing a team | ||||||
| type EditTeamOption struct { | type EditTeamOption struct { | ||||||
| 	// required: true | 	// required: true | ||||||
| 	Name        string `json:"name" binding:"Required;AlphaDashDot;MaxSize(30)"` | 	Name                    string `json:"name" binding:"Required;AlphaDashDot;MaxSize(30)"` | ||||||
| 	Description string `json:"description" binding:"MaxSize(255)"` | 	Description             string `json:"description" binding:"MaxSize(255)"` | ||||||
|  | 	IncludesAllRepositories bool   `json:"includes_all_repositories"` | ||||||
| 	// enum: read,write,admin | 	// enum: read,write,admin | ||||||
| 	Permission string `json:"permission"` | 	Permission string `json:"permission"` | ||||||
| 	// example: ["repo.code","repo.issues","repo.ext_issues","repo.wiki","repo.pulls","repo.releases","repo.ext_wiki"] | 	// example: ["repo.code","repo.issues","repo.ext_issues","repo.wiki","repo.pulls","repo.releases","repo.ext_wiki"] | ||||||
|   | |||||||
| @@ -1515,6 +1515,7 @@ team_name = Team Name | |||||||
| team_desc = Description | team_desc = Description | ||||||
| team_name_helper = Team names should be short and memorable. | team_name_helper = Team names should be short and memorable. | ||||||
| team_desc_helper = Describe the purpose or role of the team. | team_desc_helper = Describe the purpose or role of the team. | ||||||
|  | team_access_desc = Repository access | ||||||
| team_permission_desc = Permission | team_permission_desc = Permission | ||||||
| team_unit_desc = Allow Access to Repository Sections | team_unit_desc = Allow Access to Repository Sections | ||||||
|  |  | ||||||
| @@ -1588,6 +1589,13 @@ teams.add_nonexistent_repo = "The repository you're trying to add does not exist | |||||||
| teams.add_duplicate_users = User is already a team member. | teams.add_duplicate_users = User is already a team member. | ||||||
| teams.repos.none = No repositories could be accessed by this team. | teams.repos.none = No repositories could be accessed by this team. | ||||||
| teams.members.none = No members on this team. | teams.members.none = No members on this team. | ||||||
|  | teams.specific_repositories = Specific repositories | ||||||
|  | teams.specific_repositories_helper = Members will only have access to repositories explicitly added to the team. Selecting this <strong>will not</strong> automatically remove repositories already added with <i>All repositories</i>. | ||||||
|  | teams.all_repositories = All repositories | ||||||
|  | teams.all_repositories_helper = Team has access to all repositories. Selecting this will <strong>add all existing</strong> repositories to the team. | ||||||
|  | teams.all_repositories_read_permission_desc = This team grants <strong>Read</strong> access to <strong>all repositories</strong>: members can view and clone repositories. | ||||||
|  | teams.all_repositories_write_permission_desc = This team grants <strong>Write</strong> access to <strong>all repositories</strong>: members can read from and push to repositories. | ||||||
|  | teams.all_repositories_admin_permission_desc = This team grants <strong>Admin</strong> access to <strong>all repositories</strong>: members can read from, push to and add collaborators to repositories. | ||||||
|  |  | ||||||
| [admin] | [admin] | ||||||
| dashboard = Dashboard | dashboard = Dashboard | ||||||
|   | |||||||
| @@ -227,11 +227,12 @@ func ToOrganization(org *models.User) *api.Organization { | |||||||
| // ToTeam convert models.Team to api.Team | // ToTeam convert models.Team to api.Team | ||||||
| func ToTeam(team *models.Team) *api.Team { | func ToTeam(team *models.Team) *api.Team { | ||||||
| 	return &api.Team{ | 	return &api.Team{ | ||||||
| 		ID:          team.ID, | 		ID:                      team.ID, | ||||||
| 		Name:        team.Name, | 		Name:                    team.Name, | ||||||
| 		Description: team.Description, | 		Description:             team.Description, | ||||||
| 		Permission:  team.Authorize.String(), | 		IncludesAllRepositories: team.IncludesAllRepositories, | ||||||
| 		Units:       team.GetUnitNames(), | 		Permission:              team.Authorize.String(), | ||||||
|  | 		Units:                   team.GetUnitNames(), | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -128,10 +128,11 @@ func CreateTeam(ctx *context.APIContext, form api.CreateTeamOption) { | |||||||
| 	//   "201": | 	//   "201": | ||||||
| 	//     "$ref": "#/responses/Team" | 	//     "$ref": "#/responses/Team" | ||||||
| 	team := &models.Team{ | 	team := &models.Team{ | ||||||
| 		OrgID:       ctx.Org.Organization.ID, | 		OrgID:                   ctx.Org.Organization.ID, | ||||||
| 		Name:        form.Name, | 		Name:                    form.Name, | ||||||
| 		Description: form.Description, | 		Description:             form.Description, | ||||||
| 		Authorize:   models.ParseAccessMode(form.Permission), | 		IncludesAllRepositories: form.IncludesAllRepositories, | ||||||
|  | 		Authorize:               models.ParseAccessMode(form.Permission), | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	unitTypes := models.FindUnitTypes(form.Units...) | 	unitTypes := models.FindUnitTypes(form.Units...) | ||||||
| @@ -182,11 +183,27 @@ func EditTeam(ctx *context.APIContext, form api.EditTeamOption) { | |||||||
| 	//   "200": | 	//   "200": | ||||||
| 	//     "$ref": "#/responses/Team" | 	//     "$ref": "#/responses/Team" | ||||||
| 	team := ctx.Org.Team | 	team := ctx.Org.Team | ||||||
| 	team.Name = form.Name |  | ||||||
| 	team.Description = form.Description | 	team.Description = form.Description | ||||||
| 	team.Authorize = models.ParseAccessMode(form.Permission) |  | ||||||
| 	unitTypes := models.FindUnitTypes(form.Units...) | 	unitTypes := models.FindUnitTypes(form.Units...) | ||||||
|  |  | ||||||
|  | 	isAuthChanged := false | ||||||
|  | 	isIncludeAllChanged := false | ||||||
|  | 	if !team.IsOwnerTeam() { | ||||||
|  | 		// Validate permission level. | ||||||
|  | 		auth := models.ParseAccessMode(form.Permission) | ||||||
|  |  | ||||||
|  | 		team.Name = form.Name | ||||||
|  | 		if team.Authorize != auth { | ||||||
|  | 			isAuthChanged = true | ||||||
|  | 			team.Authorize = auth | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if team.IncludesAllRepositories != form.IncludesAllRepositories { | ||||||
|  | 			isIncludeAllChanged = true | ||||||
|  | 			team.IncludesAllRepositories = form.IncludesAllRepositories | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	if team.Authorize < models.AccessModeOwner { | 	if team.Authorize < models.AccessModeOwner { | ||||||
| 		var units = make([]*models.TeamUnit, 0, len(form.Units)) | 		var units = make([]*models.TeamUnit, 0, len(form.Units)) | ||||||
| 		for _, tp := range unitTypes { | 		for _, tp := range unitTypes { | ||||||
| @@ -198,7 +215,7 @@ func EditTeam(ctx *context.APIContext, form api.EditTeamOption) { | |||||||
| 		team.Units = units | 		team.Units = units | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if err := models.UpdateTeam(team, true); err != nil { | 	if err := models.UpdateTeam(team, isAuthChanged, isIncludeAllChanged); err != nil { | ||||||
| 		ctx.Error(500, "EditTeam", err) | 		ctx.Error(500, "EditTeam", err) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -1,4 +1,5 @@ | |||||||
| // Copyright 2014 The Gogs Authors. All rights reserved. | // Copyright 2014 The Gogs Authors. All rights reserved. | ||||||
|  | // Copyright 2019 The Gitea Authors. All rights reserved. | ||||||
| // Use of this source code is governed by a MIT-style | // Use of this source code is governed by a MIT-style | ||||||
| // license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
| @@ -180,12 +181,14 @@ func NewTeamPost(ctx *context.Context, form auth.CreateTeamForm) { | |||||||
| 	ctx.Data["PageIsOrgTeams"] = true | 	ctx.Data["PageIsOrgTeams"] = true | ||||||
| 	ctx.Data["PageIsOrgTeamsNew"] = true | 	ctx.Data["PageIsOrgTeamsNew"] = true | ||||||
| 	ctx.Data["Units"] = models.Units | 	ctx.Data["Units"] = models.Units | ||||||
|  | 	var includesAllRepositories = (form.RepoAccess == "all") | ||||||
|  |  | ||||||
| 	t := &models.Team{ | 	t := &models.Team{ | ||||||
| 		OrgID:       ctx.Org.Organization.ID, | 		OrgID:                   ctx.Org.Organization.ID, | ||||||
| 		Name:        form.TeamName, | 		Name:                    form.TeamName, | ||||||
| 		Description: form.Description, | 		Description:             form.Description, | ||||||
| 		Authorize:   models.ParseAccessMode(form.Permission), | 		Authorize:               models.ParseAccessMode(form.Permission), | ||||||
|  | 		IncludesAllRepositories: includesAllRepositories, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if t.Authorize < models.AccessModeOwner { | 	if t.Authorize < models.AccessModeOwner { | ||||||
| @@ -268,6 +271,8 @@ func EditTeamPost(ctx *context.Context, form auth.CreateTeamForm) { | |||||||
| 	ctx.Data["Units"] = models.Units | 	ctx.Data["Units"] = models.Units | ||||||
|  |  | ||||||
| 	isAuthChanged := false | 	isAuthChanged := false | ||||||
|  | 	isIncludeAllChanged := false | ||||||
|  | 	var includesAllRepositories = (form.RepoAccess == "all") | ||||||
| 	if !t.IsOwnerTeam() { | 	if !t.IsOwnerTeam() { | ||||||
| 		// Validate permission level. | 		// Validate permission level. | ||||||
| 		auth := models.ParseAccessMode(form.Permission) | 		auth := models.ParseAccessMode(form.Permission) | ||||||
| @@ -277,6 +282,11 @@ func EditTeamPost(ctx *context.Context, form auth.CreateTeamForm) { | |||||||
| 			isAuthChanged = true | 			isAuthChanged = true | ||||||
| 			t.Authorize = auth | 			t.Authorize = auth | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		if t.IncludesAllRepositories != includesAllRepositories { | ||||||
|  | 			isIncludeAllChanged = true | ||||||
|  | 			t.IncludesAllRepositories = includesAllRepositories | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 	t.Description = form.Description | 	t.Description = form.Description | ||||||
| 	if t.Authorize < models.AccessModeOwner { | 	if t.Authorize < models.AccessModeOwner { | ||||||
| @@ -305,7 +315,7 @@ func EditTeamPost(ctx *context.Context, form auth.CreateTeamForm) { | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if err := models.UpdateTeam(t, isAuthChanged); err != nil { | 	if err := models.UpdateTeam(t, isAuthChanged, isIncludeAllChanged); err != nil { | ||||||
| 		ctx.Data["Err_TeamName"] = true | 		ctx.Data["Err_TeamName"] = true | ||||||
| 		switch { | 		switch { | ||||||
| 		case models.IsErrTeamAlreadyExist(err): | 		case models.IsErrTeamAlreadyExist(err): | ||||||
|   | |||||||
| @@ -24,6 +24,24 @@ | |||||||
| 						<span class="help">{{.i18n.Tr "org.team_desc_helper"}}</span> | 						<span class="help">{{.i18n.Tr "org.team_desc_helper"}}</span> | ||||||
| 					</div> | 					</div> | ||||||
| 					{{if not (eq .Team.LowerName "owners")}} | 					{{if not (eq .Team.LowerName "owners")}} | ||||||
|  | 						<div class="grouped field"> | ||||||
|  | 							<label>{{.i18n.Tr "org.team_access_desc"}}</label> | ||||||
|  | 							<br> | ||||||
|  | 							<div class="field"> | ||||||
|  | 								<div class="ui radio checkbox"> | ||||||
|  | 									<input type="radio" name="repo_access" value="specific" {{if not .Team.IncludesAllRepositories}}checked{{end}}> | ||||||
|  | 									<label>{{.i18n.Tr "org.teams.specific_repositories"}}</label> | ||||||
|  | 									<span class="help">{{.i18n.Tr "org.teams.specific_repositories_helper"}}</span> | ||||||
|  | 								</div> | ||||||
|  | 							</div> | ||||||
|  | 							<div class="field"> | ||||||
|  | 								<div class="ui radio checkbox"> | ||||||
|  | 									<input type="radio" name="repo_access" value="all" {{if .Team.IncludesAllRepositories}}checked{{end}}> | ||||||
|  | 									<label>{{.i18n.Tr "org.teams.all_repositories"}}</label> | ||||||
|  | 									<span class="help">{{.i18n.Tr "org.teams.all_repositories_helper"}}</span> | ||||||
|  | 								</div> | ||||||
|  | 							</div> | ||||||
|  | 						</div> | ||||||
| 						<div class="grouped field"> | 						<div class="grouped field"> | ||||||
| 							<label>{{.i18n.Tr "org.team_permission_desc"}}</label> | 							<label>{{.i18n.Tr "org.team_permission_desc"}}</label> | ||||||
| 							<br> | 							<br> | ||||||
|   | |||||||
| @@ -7,7 +7,7 @@ | |||||||
| 			{{template "org/team/sidebar" .}} | 			{{template "org/team/sidebar" .}} | ||||||
| 			<div class="ui ten wide column"> | 			<div class="ui ten wide column"> | ||||||
| 				{{template "org/team/navbar" .}} | 				{{template "org/team/navbar" .}} | ||||||
| 				{{$canAddRemove := and $.IsOrganizationOwner (not (eq $.Team.LowerName "owners"))}} | 				{{$canAddRemove := and $.IsOrganizationOwner (not $.Team.IncludesAllRepositories)}} | ||||||
| 				{{if $canAddRemove}} | 				{{if $canAddRemove}} | ||||||
| 					<div class="ui attached segment"> | 					<div class="ui attached segment"> | ||||||
| 						<form class="ui form" id="add-repo-form" action="{{$.OrgLink}}/teams/{{$.Team.LowerName}}/action/repo/add" method="post"> | 						<form class="ui form" id="add-repo-form" action="{{$.OrgLink}}/teams/{{$.Team.LowerName}}/action/repo/add" method="post"> | ||||||
|   | |||||||
| @@ -22,11 +22,23 @@ | |||||||
| 			{{if eq .Team.LowerName "owners"}} | 			{{if eq .Team.LowerName "owners"}} | ||||||
| 				{{.i18n.Tr "org.teams.owners_permission_desc" | Str2html}} | 				{{.i18n.Tr "org.teams.owners_permission_desc" | Str2html}} | ||||||
| 			{{else if (eq .Team.Authorize 1)}} | 			{{else if (eq .Team.Authorize 1)}} | ||||||
| 				{{.i18n.Tr "org.teams.read_permission_desc" | Str2html}} | 				{{if .Team.IncludesAllRepositories}} | ||||||
|  | 					{{.i18n.Tr "org.teams.all_repositories_read_permission_desc" | Str2html}} | ||||||
|  | 				{{else}} | ||||||
|  | 					{{.i18n.Tr "org.teams.read_permission_desc" | Str2html}} | ||||||
|  | 				{{end}} | ||||||
| 			{{else if (eq .Team.Authorize 2)}} | 			{{else if (eq .Team.Authorize 2)}} | ||||||
| 				{{.i18n.Tr "org.teams.write_permission_desc" | Str2html}} | 				{{if .Team.IncludesAllRepositories}} | ||||||
|  | 					{{.i18n.Tr "org.teams.all_repositories_write_permission_desc" | Str2html}} | ||||||
|  | 				{{else}} | ||||||
|  | 					{{.i18n.Tr "org.teams.write_permission_desc" | Str2html}} | ||||||
|  | 				{{end}} | ||||||
| 			{{else if (eq .Team.Authorize 3)}} | 			{{else if (eq .Team.Authorize 3)}} | ||||||
| 				{{.i18n.Tr "org.teams.admin_permission_desc" | Str2html}} | 				{{if .Team.IncludesAllRepositories}} | ||||||
|  | 					{{.i18n.Tr "org.teams.all_repositories_admin_permission_desc" | Str2html}} | ||||||
|  | 				{{else}} | ||||||
|  | 					{{.i18n.Tr "org.teams.admin_permission_desc" | Str2html}} | ||||||
|  | 				{{end}} | ||||||
| 			{{end}} | 			{{end}} | ||||||
| 		</div> | 		</div> | ||||||
| 	</div> | 	</div> | ||||||
|   | |||||||
| @@ -8242,6 +8242,10 @@ | |||||||
|           "type": "string", |           "type": "string", | ||||||
|           "x-go-name": "Description" |           "x-go-name": "Description" | ||||||
|         }, |         }, | ||||||
|  |         "includes_all_repositories": { | ||||||
|  |           "type": "boolean", | ||||||
|  |           "x-go-name": "IncludesAllRepositories" | ||||||
|  |         }, | ||||||
|         "name": { |         "name": { | ||||||
|           "type": "string", |           "type": "string", | ||||||
|           "x-go-name": "Name" |           "x-go-name": "Name" | ||||||
| @@ -8801,6 +8805,10 @@ | |||||||
|           "type": "string", |           "type": "string", | ||||||
|           "x-go-name": "Description" |           "x-go-name": "Description" | ||||||
|         }, |         }, | ||||||
|  |         "includes_all_repositories": { | ||||||
|  |           "type": "boolean", | ||||||
|  |           "x-go-name": "IncludesAllRepositories" | ||||||
|  |         }, | ||||||
|         "name": { |         "name": { | ||||||
|           "type": "string", |           "type": "string", | ||||||
|           "x-go-name": "Name" |           "x-go-name": "Name" | ||||||
| @@ -10457,6 +10465,10 @@ | |||||||
|           "format": "int64", |           "format": "int64", | ||||||
|           "x-go-name": "ID" |           "x-go-name": "ID" | ||||||
|         }, |         }, | ||||||
|  |         "includes_all_repositories": { | ||||||
|  |           "type": "boolean", | ||||||
|  |           "x-go-name": "IncludesAllRepositories" | ||||||
|  |         }, | ||||||
|         "name": { |         "name": { | ||||||
|           "type": "string", |           "type": "string", | ||||||
|           "x-go-name": "Name" |           "x-go-name": "Name" | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user