mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-29 10:57:44 +09:00 
			
		
		
		
	Fix team permission (#34128)
The `team.access_mode` should be either `none` or `admin/owner`. For non-admin team, the real permissions are provided by `team_unit`.
This commit is contained in:
		| @@ -11,7 +11,6 @@ import ( | |||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	"code.gitea.io/gitea/models/organization" | 	"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" | ||||||
| 	project_model "code.gitea.io/gitea/models/project" | 	project_model "code.gitea.io/gitea/models/project" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| @@ -612,7 +611,7 @@ func ResolveIssueMentionsByVisibility(ctx context.Context, issue *Issue, doer *u | |||||||
| 				unittype = unit.TypePullRequests | 				unittype = unit.TypePullRequests | ||||||
| 			} | 			} | ||||||
| 			for _, team := range teams { | 			for _, team := range teams { | ||||||
| 				if team.AccessMode >= perm.AccessModeAdmin { | 				if team.HasAdminAccess() { | ||||||
| 					checked = append(checked, team.ID) | 					checked = append(checked, team.ID) | ||||||
| 					resolved[issue.Repo.Owner.LowerName+"/"+team.LowerName] = true | 					resolved[issue.Repo.Owner.LowerName+"/"+team.LowerName] = true | ||||||
| 					continue | 					continue | ||||||
|   | |||||||
| @@ -78,7 +78,7 @@ func IsOrganizationAdmin(ctx context.Context, orgID, uid int64) (bool, error) { | |||||||
| 		return false, err | 		return false, err | ||||||
| 	} | 	} | ||||||
| 	for _, t := range teams { | 	for _, t := range teams { | ||||||
| 		if t.AccessMode >= perm.AccessModeAdmin { | 		if t.HasAdminAccess() { | ||||||
| 			return true, nil | 			return true, nil | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -113,7 +113,7 @@ func (t *Team) LoadUnits(ctx context.Context) (err error) { | |||||||
|  |  | ||||||
| // GetUnitNames returns the team units names | // GetUnitNames returns the team units names | ||||||
| func (t *Team) GetUnitNames() (res []string) { | func (t *Team) GetUnitNames() (res []string) { | ||||||
| 	if t.AccessMode >= perm.AccessModeAdmin { | 	if t.HasAdminAccess() { | ||||||
| 		return unit.AllUnitKeyNames() | 		return unit.AllUnitKeyNames() | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -126,7 +126,7 @@ func (t *Team) GetUnitNames() (res []string) { | |||||||
| // GetUnitsMap returns the team units permissions | // GetUnitsMap returns the team units permissions | ||||||
| func (t *Team) GetUnitsMap() map[string]string { | func (t *Team) GetUnitsMap() map[string]string { | ||||||
| 	m := make(map[string]string) | 	m := make(map[string]string) | ||||||
| 	if t.AccessMode >= perm.AccessModeAdmin { | 	if t.HasAdminAccess() { | ||||||
| 		for _, u := range unit.Units { | 		for _, u := range unit.Units { | ||||||
| 			m[u.NameKey] = t.AccessMode.ToString() | 			m[u.NameKey] = t.AccessMode.ToString() | ||||||
| 		} | 		} | ||||||
| @@ -153,6 +153,10 @@ func (t *Team) IsMember(ctx context.Context, userID int64) bool { | |||||||
| 	return isMember | 	return isMember | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (t *Team) HasAdminAccess() bool { | ||||||
|  | 	return t.AccessMode >= perm.AccessModeAdmin | ||||||
|  | } | ||||||
|  |  | ||||||
| // LoadMembers returns paginated members in team of organization. | // LoadMembers returns paginated members in team of organization. | ||||||
| func (t *Team) LoadMembers(ctx context.Context) (err error) { | func (t *Team) LoadMembers(ctx context.Context) (err error) { | ||||||
| 	t.Members, err = GetTeamMembers(ctx, &SearchMembersOptions{ | 	t.Members, err = GetTeamMembers(ctx, &SearchMembersOptions{ | ||||||
| @@ -238,22 +242,6 @@ func GetTeamByID(ctx context.Context, teamID int64) (*Team, error) { | |||||||
| 	return t, nil | 	return t, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // GetTeamNamesByID returns team's lower name from a list of team ids. |  | ||||||
| func GetTeamNamesByID(ctx context.Context, teamIDs []int64) ([]string, error) { |  | ||||||
| 	if len(teamIDs) == 0 { |  | ||||||
| 		return []string{}, nil |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	var teamNames []string |  | ||||||
| 	err := db.GetEngine(ctx).Table("team"). |  | ||||||
| 		Select("lower_name"). |  | ||||||
| 		In("id", teamIDs). |  | ||||||
| 		Asc("name"). |  | ||||||
| 		Find(&teamNames) |  | ||||||
|  |  | ||||||
| 	return teamNames, err |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // IncrTeamRepoNum increases the number of repos for the given team by 1 | // IncrTeamRepoNum increases the number of repos for the given team by 1 | ||||||
| func IncrTeamRepoNum(ctx context.Context, teamID int64) error { | func IncrTeamRepoNum(ctx context.Context, teamID int64) error { | ||||||
| 	_, err := db.GetEngine(ctx).Incr("num_repos").ID(teamID).Update(new(Team)) | 	_, err := db.GetEngine(ctx).Incr("num_repos").ID(teamID).Update(new(Team)) | ||||||
|   | |||||||
| @@ -331,7 +331,7 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use | |||||||
|  |  | ||||||
| 	// if user in an owner team | 	// if user in an owner team | ||||||
| 	for _, team := range teams { | 	for _, team := range teams { | ||||||
| 		if team.AccessMode >= perm_model.AccessModeAdmin { | 		if team.HasAdminAccess() { | ||||||
| 			perm.AccessMode = perm_model.AccessModeOwner | 			perm.AccessMode = perm_model.AccessModeOwner | ||||||
| 			perm.unitsMode = nil | 			perm.unitsMode = nil | ||||||
| 			return perm, nil | 			return perm, nil | ||||||
| @@ -399,7 +399,7 @@ func IsUserRepoAdmin(ctx context.Context, repo *repo_model.Repository, user *use | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	for _, team := range teams { | 	for _, team := range teams { | ||||||
| 		if team.AccessMode >= perm_model.AccessModeAdmin { | 		if team.HasAdminAccess() { | ||||||
| 			return true, nil | 			return true, nil | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -20,17 +20,21 @@ type Type int | |||||||
|  |  | ||||||
| // Enumerate all the unit types | // Enumerate all the unit types | ||||||
| const ( | const ( | ||||||
| 	TypeInvalid         Type = iota // 0 invalid | 	TypeInvalid Type = iota // 0 invalid | ||||||
| 	TypeCode                        // 1 code |  | ||||||
| 	TypeIssues                      // 2 issues | 	TypeCode            // 1 code | ||||||
| 	TypePullRequests                // 3 PRs | 	TypeIssues          // 2 issues | ||||||
| 	TypeReleases                    // 4 Releases | 	TypePullRequests    // 3 PRs | ||||||
| 	TypeWiki                        // 5 Wiki | 	TypeReleases        // 4 Releases | ||||||
| 	TypeExternalWiki                // 6 ExternalWiki | 	TypeWiki            // 5 Wiki | ||||||
| 	TypeExternalTracker             // 7 ExternalTracker | 	TypeExternalWiki    // 6 ExternalWiki | ||||||
| 	TypeProjects                    // 8 Projects | 	TypeExternalTracker // 7 ExternalTracker | ||||||
| 	TypePackages                    // 9 Packages | 	TypeProjects        // 8 Projects | ||||||
| 	TypeActions                     // 10 Actions | 	TypePackages        // 9 Packages | ||||||
|  | 	TypeActions         // 10 Actions | ||||||
|  |  | ||||||
|  | 	// FIXME: TEAM-UNIT-PERMISSION: the team unit "admin" permission's design is not right, when a new unit is added in the future, | ||||||
|  | 	// admin team won't inherit the correct admin permission for the new unit, need to have a complete fix before adding any new unit. | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // Value returns integer value for unit type (used by template) | // Value returns integer value for unit type (used by template) | ||||||
| @@ -380,20 +384,3 @@ func AllUnitKeyNames() []string { | |||||||
| 	} | 	} | ||||||
| 	return res | 	return res | ||||||
| } | } | ||||||
|  |  | ||||||
| // MinUnitAccessMode returns the minial permission of the permission map |  | ||||||
| func MinUnitAccessMode(unitsMap map[Type]perm.AccessMode) perm.AccessMode { |  | ||||||
| 	res := perm.AccessModeNone |  | ||||||
| 	for t, mode := range unitsMap { |  | ||||||
| 		// Don't allow `TypeExternal{Tracker,Wiki}` to influence this as they can only be set to READ perms. |  | ||||||
| 		if t == TypeExternalTracker || t == TypeExternalWiki { |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		// get the minial permission great than AccessModeNone except all are AccessModeNone |  | ||||||
| 		if mode > perm.AccessModeNone && (res == perm.AccessModeNone || mode < res) { |  | ||||||
| 			res = mode |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return res |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -141,26 +141,18 @@ func GetTeam(ctx *context.APIContext) { | |||||||
| 	ctx.JSON(http.StatusOK, apiTeam) | 	ctx.JSON(http.StatusOK, apiTeam) | ||||||
| } | } | ||||||
|  |  | ||||||
| func attachTeamUnits(team *organization.Team, units []string) { | func attachTeamUnits(team *organization.Team, defaultAccessMode perm.AccessMode, units []string) { | ||||||
| 	unitTypes, _ := unit_model.FindUnitTypes(units...) | 	unitTypes, _ := unit_model.FindUnitTypes(units...) | ||||||
| 	team.Units = make([]*organization.TeamUnit, 0, len(units)) | 	team.Units = make([]*organization.TeamUnit, 0, len(units)) | ||||||
| 	for _, tp := range unitTypes { | 	for _, tp := range unitTypes { | ||||||
| 		team.Units = append(team.Units, &organization.TeamUnit{ | 		team.Units = append(team.Units, &organization.TeamUnit{ | ||||||
| 			OrgID:      team.OrgID, | 			OrgID:      team.OrgID, | ||||||
| 			Type:       tp, | 			Type:       tp, | ||||||
| 			AccessMode: team.AccessMode, | 			AccessMode: defaultAccessMode, | ||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func convertUnitsMap(unitsMap map[string]string) map[unit_model.Type]perm.AccessMode { |  | ||||||
| 	res := make(map[unit_model.Type]perm.AccessMode, len(unitsMap)) |  | ||||||
| 	for unitKey, p := range unitsMap { |  | ||||||
| 		res[unit_model.TypeFromKey(unitKey)] = perm.ParseAccessMode(p) |  | ||||||
| 	} |  | ||||||
| 	return res |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func attachTeamUnitsMap(team *organization.Team, unitsMap map[string]string) { | func attachTeamUnitsMap(team *organization.Team, unitsMap map[string]string) { | ||||||
| 	team.Units = make([]*organization.TeamUnit, 0, len(unitsMap)) | 	team.Units = make([]*organization.TeamUnit, 0, len(unitsMap)) | ||||||
| 	for unitKey, p := range unitsMap { | 	for unitKey, p := range unitsMap { | ||||||
| @@ -214,24 +206,22 @@ func CreateTeam(ctx *context.APIContext) { | |||||||
| 	//   "422": | 	//   "422": | ||||||
| 	//     "$ref": "#/responses/validationError" | 	//     "$ref": "#/responses/validationError" | ||||||
| 	form := web.GetForm(ctx).(*api.CreateTeamOption) | 	form := web.GetForm(ctx).(*api.CreateTeamOption) | ||||||
| 	p := perm.ParseAccessMode(form.Permission) | 	teamPermission := perm.ParseAccessMode(form.Permission, perm.AccessModeNone, perm.AccessModeAdmin) | ||||||
| 	if p < perm.AccessModeAdmin && len(form.UnitsMap) > 0 { |  | ||||||
| 		p = unit_model.MinUnitAccessMode(convertUnitsMap(form.UnitsMap)) |  | ||||||
| 	} |  | ||||||
| 	team := &organization.Team{ | 	team := &organization.Team{ | ||||||
| 		OrgID:                   ctx.Org.Organization.ID, | 		OrgID:                   ctx.Org.Organization.ID, | ||||||
| 		Name:                    form.Name, | 		Name:                    form.Name, | ||||||
| 		Description:             form.Description, | 		Description:             form.Description, | ||||||
| 		IncludesAllRepositories: form.IncludesAllRepositories, | 		IncludesAllRepositories: form.IncludesAllRepositories, | ||||||
| 		CanCreateOrgRepo:        form.CanCreateOrgRepo, | 		CanCreateOrgRepo:        form.CanCreateOrgRepo, | ||||||
| 		AccessMode:              p, | 		AccessMode:              teamPermission, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if team.AccessMode < perm.AccessModeAdmin { | 	if team.AccessMode < perm.AccessModeAdmin { | ||||||
| 		if len(form.UnitsMap) > 0 { | 		if len(form.UnitsMap) > 0 { | ||||||
| 			attachTeamUnitsMap(team, form.UnitsMap) | 			attachTeamUnitsMap(team, form.UnitsMap) | ||||||
| 		} else if len(form.Units) > 0 { | 		} else if len(form.Units) > 0 { | ||||||
| 			attachTeamUnits(team, form.Units) | 			unitPerm := perm.ParseAccessMode(form.Permission, perm.AccessModeRead, perm.AccessModeWrite) | ||||||
|  | 			attachTeamUnits(team, unitPerm, form.Units) | ||||||
| 		} else { | 		} else { | ||||||
| 			ctx.APIErrorInternal(errors.New("units permission should not be empty")) | 			ctx.APIErrorInternal(errors.New("units permission should not be empty")) | ||||||
| 			return | 			return | ||||||
| @@ -304,15 +294,10 @@ func EditTeam(ctx *context.APIContext) { | |||||||
| 	isAuthChanged := false | 	isAuthChanged := false | ||||||
| 	isIncludeAllChanged := false | 	isIncludeAllChanged := false | ||||||
| 	if !team.IsOwnerTeam() && len(form.Permission) != 0 { | 	if !team.IsOwnerTeam() && len(form.Permission) != 0 { | ||||||
| 		// Validate permission level. | 		teamPermission := perm.ParseAccessMode(form.Permission, perm.AccessModeNone, perm.AccessModeAdmin) | ||||||
| 		p := perm.ParseAccessMode(form.Permission) | 		if team.AccessMode != teamPermission { | ||||||
| 		if p < perm.AccessModeAdmin && len(form.UnitsMap) > 0 { |  | ||||||
| 			p = unit_model.MinUnitAccessMode(convertUnitsMap(form.UnitsMap)) |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		if team.AccessMode != p { |  | ||||||
| 			isAuthChanged = true | 			isAuthChanged = true | ||||||
| 			team.AccessMode = p | 			team.AccessMode = teamPermission | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if form.IncludesAllRepositories != nil { | 		if form.IncludesAllRepositories != nil { | ||||||
| @@ -325,7 +310,8 @@ func EditTeam(ctx *context.APIContext) { | |||||||
| 		if len(form.UnitsMap) > 0 { | 		if len(form.UnitsMap) > 0 { | ||||||
| 			attachTeamUnitsMap(team, form.UnitsMap) | 			attachTeamUnitsMap(team, form.UnitsMap) | ||||||
| 		} else if len(form.Units) > 0 { | 		} else if len(form.Units) > 0 { | ||||||
| 			attachTeamUnits(team, form.Units) | 			unitPerm := perm.ParseAccessMode(form.Permission, perm.AccessModeRead, perm.AccessModeWrite) | ||||||
|  | 			attachTeamUnits(team, unitPerm, form.Units) | ||||||
| 		} | 		} | ||||||
| 	} else { | 	} else { | ||||||
| 		attachAdminTeamUnits(team) | 		attachAdminTeamUnits(team) | ||||||
|   | |||||||
| @@ -181,7 +181,7 @@ func AddOrUpdateCollaborator(ctx *context.APIContext) { | |||||||
|  |  | ||||||
| 	p := perm.AccessModeWrite | 	p := perm.AccessModeWrite | ||||||
| 	if form.Permission != nil { | 	if form.Permission != nil { | ||||||
| 		p = perm.ParseAccessMode(*form.Permission) | 		p = perm.ParseAccessMode(*form.Permission, perm.AccessModeRead, perm.AccessModeWrite, perm.AccessModeAdmin) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if err := repo_service.AddOrUpdateCollaborator(ctx, ctx.Repo.Repository, collaborator, p); err != nil { | 	if err := repo_service.AddOrUpdateCollaborator(ctx, ctx.Repo.Repository, collaborator, p); err != nil { | ||||||
|   | |||||||
| @@ -284,6 +284,8 @@ func NewTeam(ctx *context.Context) { | |||||||
| 	ctx.HTML(http.StatusOK, tplTeamNew) | 	ctx.HTML(http.StatusOK, tplTeamNew) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // FIXME: TEAM-UNIT-PERMISSION: this design is not right, when a new unit is added in the future, | ||||||
|  | // admin team won't inherit the correct admin permission for the new unit. | ||||||
| func getUnitPerms(forms url.Values, teamPermission perm.AccessMode) map[unit_model.Type]perm.AccessMode { | func getUnitPerms(forms url.Values, teamPermission perm.AccessMode) map[unit_model.Type]perm.AccessMode { | ||||||
| 	unitPerms := make(map[unit_model.Type]perm.AccessMode) | 	unitPerms := make(map[unit_model.Type]perm.AccessMode) | ||||||
| 	for _, ut := range unit_model.AllRepoUnitTypes { | 	for _, ut := range unit_model.AllRepoUnitTypes { | ||||||
| @@ -314,19 +316,14 @@ func getUnitPerms(forms url.Values, teamPermission perm.AccessMode) map[unit_mod | |||||||
| func NewTeamPost(ctx *context.Context) { | func NewTeamPost(ctx *context.Context) { | ||||||
| 	form := web.GetForm(ctx).(*forms.CreateTeamForm) | 	form := web.GetForm(ctx).(*forms.CreateTeamForm) | ||||||
| 	includesAllRepositories := form.RepoAccess == "all" | 	includesAllRepositories := form.RepoAccess == "all" | ||||||
| 	p := perm.ParseAccessMode(form.Permission) | 	teamPermission := perm.ParseAccessMode(form.Permission, perm.AccessModeNone, perm.AccessModeAdmin) | ||||||
| 	unitPerms := getUnitPerms(ctx.Req.Form, p) | 	unitPerms := getUnitPerms(ctx.Req.Form, teamPermission) | ||||||
| 	if p < perm.AccessModeAdmin { |  | ||||||
| 		// if p is less than admin accessmode, then it should be general accessmode, |  | ||||||
| 		// so we should calculate the minial accessmode from units accessmodes. |  | ||||||
| 		p = unit_model.MinUnitAccessMode(unitPerms) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	t := &org_model.Team{ | 	t := &org_model.Team{ | ||||||
| 		OrgID:                   ctx.Org.Organization.ID, | 		OrgID:                   ctx.Org.Organization.ID, | ||||||
| 		Name:                    form.TeamName, | 		Name:                    form.TeamName, | ||||||
| 		Description:             form.Description, | 		Description:             form.Description, | ||||||
| 		AccessMode:              p, | 		AccessMode:              teamPermission, | ||||||
| 		IncludesAllRepositories: includesAllRepositories, | 		IncludesAllRepositories: includesAllRepositories, | ||||||
| 		CanCreateOrgRepo:        form.CanCreateOrgRepo, | 		CanCreateOrgRepo:        form.CanCreateOrgRepo, | ||||||
| 	} | 	} | ||||||
| @@ -485,13 +482,8 @@ func EditTeam(ctx *context.Context) { | |||||||
| func EditTeamPost(ctx *context.Context) { | func EditTeamPost(ctx *context.Context) { | ||||||
| 	form := web.GetForm(ctx).(*forms.CreateTeamForm) | 	form := web.GetForm(ctx).(*forms.CreateTeamForm) | ||||||
| 	t := ctx.Org.Team | 	t := ctx.Org.Team | ||||||
| 	newAccessMode := perm.ParseAccessMode(form.Permission) | 	teamPermission := perm.ParseAccessMode(form.Permission, perm.AccessModeNone, perm.AccessModeAdmin) | ||||||
| 	unitPerms := getUnitPerms(ctx.Req.Form, newAccessMode) | 	unitPerms := getUnitPerms(ctx.Req.Form, teamPermission) | ||||||
| 	if newAccessMode < perm.AccessModeAdmin { |  | ||||||
| 		// if newAccessMode is less than admin accessmode, then it should be general accessmode, |  | ||||||
| 		// so we should calculate the minial accessmode from units accessmodes. |  | ||||||
| 		newAccessMode = unit_model.MinUnitAccessMode(unitPerms) |  | ||||||
| 	} |  | ||||||
| 	isAuthChanged := false | 	isAuthChanged := false | ||||||
| 	isIncludeAllChanged := false | 	isIncludeAllChanged := false | ||||||
| 	includesAllRepositories := form.RepoAccess == "all" | 	includesAllRepositories := form.RepoAccess == "all" | ||||||
| @@ -503,9 +495,9 @@ func EditTeamPost(ctx *context.Context) { | |||||||
|  |  | ||||||
| 	if !t.IsOwnerTeam() { | 	if !t.IsOwnerTeam() { | ||||||
| 		t.Name = form.TeamName | 		t.Name = form.TeamName | ||||||
| 		if t.AccessMode != newAccessMode { | 		if t.AccessMode != teamPermission { | ||||||
| 			isAuthChanged = true | 			isAuthChanged = true | ||||||
| 			t.AccessMode = newAccessMode | 			t.AccessMode = teamPermission | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if t.IncludesAllRepositories != includesAllRepositories { | 		if t.IncludesAllRepositories != includesAllRepositories { | ||||||
|   | |||||||
| @@ -182,7 +182,7 @@ func OrgAssignment(opts OrgAssignmentOptions) func(ctx *Context) { | |||||||
| 					return | 					return | ||||||
| 				} | 				} | ||||||
| 				for _, team := range teams { | 				for _, team := range teams { | ||||||
| 					if team.IncludesAllRepositories && team.AccessMode >= perm.AccessModeAdmin { | 					if team.IncludesAllRepositories && team.HasAdminAccess() { | ||||||
| 						shouldSeeAllTeams = true | 						shouldSeeAllTeams = true | ||||||
| 						break | 						break | ||||||
| 					} | 					} | ||||||
| @@ -228,7 +228,7 @@ func OrgAssignment(opts OrgAssignmentOptions) func(ctx *Context) { | |||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			ctx.Org.IsTeamAdmin = ctx.Org.Team.IsOwnerTeam() || ctx.Org.Team.AccessMode >= perm.AccessModeAdmin | 			ctx.Org.IsTeamAdmin = ctx.Org.Team.IsOwnerTeam() || ctx.Org.Team.HasAdminAccess() | ||||||
| 			ctx.Data["IsTeamAdmin"] = ctx.Org.IsTeamAdmin | 			ctx.Data["IsTeamAdmin"] = ctx.Org.IsTeamAdmin | ||||||
| 			if opts.RequireTeamAdmin && !ctx.Org.IsTeamAdmin { | 			if opts.RequireTeamAdmin && !ctx.Org.IsTeamAdmin { | ||||||
| 				ctx.NotFound(err) | 				ctx.NotFound(err) | ||||||
|   | |||||||
| @@ -259,37 +259,6 @@ func AddTeamMember(ctx context.Context, team *organization.Team, user *user_mode | |||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		team.NumMembers++ | 		team.NumMembers++ | ||||||
|  |  | ||||||
| 		// Give access to team repositories. |  | ||||||
| 		// update exist access if mode become bigger |  | ||||||
| 		subQuery := builder.Select("repo_id").From("team_repo"). |  | ||||||
| 			Where(builder.Eq{"team_id": team.ID}) |  | ||||||
|  |  | ||||||
| 		if _, err := sess.Where("user_id=?", user.ID). |  | ||||||
| 			In("repo_id", subQuery). |  | ||||||
| 			And("mode < ?", team.AccessMode). |  | ||||||
| 			SetExpr("mode", team.AccessMode). |  | ||||||
| 			Update(new(access_model.Access)); err != nil { |  | ||||||
| 			return fmt.Errorf("update user accesses: %w", err) |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		// for not exist access |  | ||||||
| 		var repoIDs []int64 |  | ||||||
| 		accessSubQuery := builder.Select("repo_id").From("access").Where(builder.Eq{"user_id": user.ID}) |  | ||||||
| 		if err := sess.SQL(subQuery.And(builder.NotIn("repo_id", accessSubQuery))).Find(&repoIDs); err != nil { |  | ||||||
| 			return fmt.Errorf("select id accesses: %w", err) |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		accesses := make([]*access_model.Access, 0, 100) |  | ||||||
| 		for i, repoID := range repoIDs { |  | ||||||
| 			accesses = append(accesses, &access_model.Access{RepoID: repoID, UserID: user.ID, Mode: team.AccessMode}) |  | ||||||
| 			if (i%100 == 0 || i == len(repoIDs)-1) && len(accesses) > 0 { |  | ||||||
| 				if err = db.Insert(ctx, accesses); err != nil { |  | ||||||
| 					return fmt.Errorf("insert new user accesses: %w", err) |  | ||||||
| 				} |  | ||||||
| 				accesses = accesses[:0] |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		return nil | 		return nil | ||||||
| 	}) | 	}) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
|   | |||||||
| @@ -166,24 +166,6 @@ func TestRemoveTeamMember(t *testing.T) { | |||||||
| 	assert.True(t, organization.IsErrLastOrgOwner(err)) | 	assert.True(t, organization.IsErrLastOrgOwner(err)) | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestRepository_RecalculateAccesses3(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
| 	team5 := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 5}) |  | ||||||
| 	user29 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 29}) |  | ||||||
|  |  | ||||||
| 	has, err := db.GetEngine(db.DefaultContext).Get(&access_model.Access{UserID: user29.ID, RepoID: 23}) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	assert.False(t, has) |  | ||||||
|  |  | ||||||
| 	// adding user29 to team5 should add an explicit access row for repo 23 |  | ||||||
| 	// even though repo 23 is public |  | ||||||
| 	assert.NoError(t, AddTeamMember(db.DefaultContext, team5, user29)) |  | ||||||
|  |  | ||||||
| 	has, err = db.GetEngine(db.DefaultContext).Get(&access_model.Access{UserID: user29.ID, RepoID: 23}) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	assert.True(t, has) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestIncludesAllRepositoriesTeams(t *testing.T) { | func TestIncludesAllRepositoriesTeams(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -56,7 +56,7 @@ | |||||||
| 								<br> | 								<br> | ||||||
| 								<div class="field"> | 								<div class="field"> | ||||||
| 									<div class="ui radio checkbox"> | 									<div class="ui radio checkbox"> | ||||||
| 										<input type="radio" name="permission" value="read" {{if or .PageIsOrgTeamsNew (eq .Team.AccessMode 1) (eq .Team.AccessMode 2)}}checked{{end}}> | 										<input type="radio" name="permission" value="read" {{if or .PageIsOrgTeamsNew (eq .Team.AccessMode 0) (eq .Team.AccessMode 1) (eq .Team.AccessMode 2)}}checked{{end}}> | ||||||
| 										<label>{{ctx.Locale.Tr "org.teams.general_access"}}</label> | 										<label>{{ctx.Locale.Tr "org.teams.general_access"}}</label> | ||||||
| 										<span class="help">{{ctx.Locale.Tr "org.teams.general_access_helper"}}</span> | 										<span class="help">{{ctx.Locale.Tr "org.teams.general_access_helper"}}</span> | ||||||
| 									</div> | 									</div> | ||||||
|   | |||||||
| @@ -42,10 +42,12 @@ | |||||||
| 						<li>{{ctx.Locale.Tr "org.teams.can_create_org_repo"}}</li> | 						<li>{{ctx.Locale.Tr "org.teams.can_create_org_repo"}}</li> | ||||||
| 					{{end}} | 					{{end}} | ||||||
| 				</ul> | 				</ul> | ||||||
| 				{{if (eq .Team.AccessMode 2)}} | 				{{/* the AccessMode should be either none or admin/owner, the real permissions are provided by each team unit */}} | ||||||
|  | 				{{if false}}{{/*(eq .Team.AccessMode 2)*/}} | ||||||
| 					<h3>{{ctx.Locale.Tr "org.settings.permission"}}</h3> | 					<h3>{{ctx.Locale.Tr "org.settings.permission"}}</h3> | ||||||
| 					{{ctx.Locale.Tr "org.teams.write_permission_desc"}} | 					{{ctx.Locale.Tr "org.teams.write_permission_desc"}} | ||||||
| 				{{else if (eq .Team.AccessMode 3)}} | 				{{else if (eq .Team.AccessMode 3)}} | ||||||
|  | 					{{/* FIXME: here might not right, see "FIXME: TEAM-UNIT-PERMISSION", new units might not have correct admin permission*/}} | ||||||
| 					<h3>{{ctx.Locale.Tr "org.settings.permission"}}</h3> | 					<h3>{{ctx.Locale.Tr "org.settings.permission"}}</h3> | ||||||
| 					{{ctx.Locale.Tr "org.teams.admin_permission_desc"}} | 					{{ctx.Locale.Tr "org.teams.admin_permission_desc"}} | ||||||
| 				{{else}} | 				{{else}} | ||||||
|   | |||||||
| @@ -78,9 +78,9 @@ func TestAPITeam(t *testing.T) { | |||||||
| 	apiTeam = api.Team{} | 	apiTeam = api.Team{} | ||||||
| 	DecodeJSON(t, resp, &apiTeam) | 	DecodeJSON(t, resp, &apiTeam) | ||||||
| 	checkTeamResponse(t, "CreateTeam1", &apiTeam, teamToCreate.Name, teamToCreate.Description, teamToCreate.IncludesAllRepositories, | 	checkTeamResponse(t, "CreateTeam1", &apiTeam, teamToCreate.Name, teamToCreate.Description, teamToCreate.IncludesAllRepositories, | ||||||
| 		teamToCreate.Permission, teamToCreate.Units, nil) | 		"none", teamToCreate.Units, nil) | ||||||
| 	checkTeamBean(t, apiTeam.ID, teamToCreate.Name, teamToCreate.Description, teamToCreate.IncludesAllRepositories, | 	checkTeamBean(t, apiTeam.ID, teamToCreate.Name, teamToCreate.Description, teamToCreate.IncludesAllRepositories, | ||||||
| 		teamToCreate.Permission, teamToCreate.Units, nil) | 		"none", teamToCreate.Units, nil) | ||||||
| 	teamID := apiTeam.ID | 	teamID := apiTeam.ID | ||||||
|  |  | ||||||
| 	// Edit team. | 	// Edit team. | ||||||
| @@ -149,9 +149,9 @@ func TestAPITeam(t *testing.T) { | |||||||
| 	apiTeam = api.Team{} | 	apiTeam = api.Team{} | ||||||
| 	DecodeJSON(t, resp, &apiTeam) | 	DecodeJSON(t, resp, &apiTeam) | ||||||
| 	checkTeamResponse(t, "CreateTeam2", &apiTeam, teamToCreate.Name, teamToCreate.Description, teamToCreate.IncludesAllRepositories, | 	checkTeamResponse(t, "CreateTeam2", &apiTeam, teamToCreate.Name, teamToCreate.Description, teamToCreate.IncludesAllRepositories, | ||||||
| 		"read", nil, teamToCreate.UnitsMap) | 		"none", nil, teamToCreate.UnitsMap) | ||||||
| 	checkTeamBean(t, apiTeam.ID, teamToCreate.Name, teamToCreate.Description, teamToCreate.IncludesAllRepositories, | 	checkTeamBean(t, apiTeam.ID, teamToCreate.Name, teamToCreate.Description, teamToCreate.IncludesAllRepositories, | ||||||
| 		"read", nil, teamToCreate.UnitsMap) | 		"none", nil, teamToCreate.UnitsMap) | ||||||
| 	teamID = apiTeam.ID | 	teamID = apiTeam.ID | ||||||
|  |  | ||||||
| 	// Edit team. | 	// Edit team. | ||||||
| @@ -171,9 +171,9 @@ func TestAPITeam(t *testing.T) { | |||||||
| 	apiTeam = api.Team{} | 	apiTeam = api.Team{} | ||||||
| 	DecodeJSON(t, resp, &apiTeam) | 	DecodeJSON(t, resp, &apiTeam) | ||||||
| 	checkTeamResponse(t, "EditTeam2", &apiTeam, teamToEdit.Name, *teamToEdit.Description, *teamToEdit.IncludesAllRepositories, | 	checkTeamResponse(t, "EditTeam2", &apiTeam, teamToEdit.Name, *teamToEdit.Description, *teamToEdit.IncludesAllRepositories, | ||||||
| 		"read", nil, teamToEdit.UnitsMap) | 		"none", nil, teamToEdit.UnitsMap) | ||||||
| 	checkTeamBean(t, apiTeam.ID, teamToEdit.Name, *teamToEdit.Description, *teamToEdit.IncludesAllRepositories, | 	checkTeamBean(t, apiTeam.ID, teamToEdit.Name, *teamToEdit.Description, *teamToEdit.IncludesAllRepositories, | ||||||
| 		"read", nil, teamToEdit.UnitsMap) | 		"none", nil, teamToEdit.UnitsMap) | ||||||
|  |  | ||||||
| 	// Edit team Description only | 	// Edit team Description only | ||||||
| 	editDescription = "second team" | 	editDescription = "second team" | ||||||
| @@ -184,9 +184,9 @@ func TestAPITeam(t *testing.T) { | |||||||
| 	apiTeam = api.Team{} | 	apiTeam = api.Team{} | ||||||
| 	DecodeJSON(t, resp, &apiTeam) | 	DecodeJSON(t, resp, &apiTeam) | ||||||
| 	checkTeamResponse(t, "EditTeam2_DescOnly", &apiTeam, teamToEdit.Name, *teamToEditDesc.Description, *teamToEdit.IncludesAllRepositories, | 	checkTeamResponse(t, "EditTeam2_DescOnly", &apiTeam, teamToEdit.Name, *teamToEditDesc.Description, *teamToEdit.IncludesAllRepositories, | ||||||
| 		"read", nil, teamToEdit.UnitsMap) | 		"none", nil, teamToEdit.UnitsMap) | ||||||
| 	checkTeamBean(t, apiTeam.ID, teamToEdit.Name, *teamToEditDesc.Description, *teamToEdit.IncludesAllRepositories, | 	checkTeamBean(t, apiTeam.ID, teamToEdit.Name, *teamToEditDesc.Description, *teamToEdit.IncludesAllRepositories, | ||||||
| 		"read", nil, teamToEdit.UnitsMap) | 		"none", nil, teamToEdit.UnitsMap) | ||||||
|  |  | ||||||
| 	// Read team. | 	// Read team. | ||||||
| 	teamRead = unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID}) | 	teamRead = unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID}) | ||||||
|   | |||||||
| @@ -177,9 +177,9 @@ func TestOrgRestrictedUser(t *testing.T) { | |||||||
| 	resp := adminSession.MakeRequest(t, req, http.StatusCreated) | 	resp := adminSession.MakeRequest(t, req, http.StatusCreated) | ||||||
| 	DecodeJSON(t, resp, &apiTeam) | 	DecodeJSON(t, resp, &apiTeam) | ||||||
| 	checkTeamResponse(t, "CreateTeam_codereader", &apiTeam, teamToCreate.Name, teamToCreate.Description, teamToCreate.IncludesAllRepositories, | 	checkTeamResponse(t, "CreateTeam_codereader", &apiTeam, teamToCreate.Name, teamToCreate.Description, teamToCreate.IncludesAllRepositories, | ||||||
| 		teamToCreate.Permission, teamToCreate.Units, nil) | 		"none", teamToCreate.Units, nil) | ||||||
| 	checkTeamBean(t, apiTeam.ID, teamToCreate.Name, teamToCreate.Description, teamToCreate.IncludesAllRepositories, | 	checkTeamBean(t, apiTeam.ID, teamToCreate.Name, teamToCreate.Description, teamToCreate.IncludesAllRepositories, | ||||||
| 		teamToCreate.Permission, teamToCreate.Units, nil) | 		"none", teamToCreate.Units, nil) | ||||||
| 	// teamID := apiTeam.ID | 	// teamID := apiTeam.ID | ||||||
|  |  | ||||||
| 	// Now we need to add the restricted user to the team | 	// Now we need to add the restricted user to the team | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user