Skip to content
Draft
5 changes: 5 additions & 0 deletions models/organization/team.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,3 +272,8 @@ func IncrTeamRepoNum(ctx context.Context, teamID int64) error {
_, err := db.GetEngine(ctx).Incr("num_repos").ID(teamID).Update(new(Team))
return err
}

func UpdateTeam(ctx context.Context, t *Team, cols ...string) error {
_, err := db.GetEngine(ctx).ID(t.ID).Cols(cols...).Update(t)
return err
}
72 changes: 72 additions & 0 deletions models/organization/team_fix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package organization

import (
"context"
"strings"

"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/perm"
"code.gitea.io/gitea/models/unit"

"xorm.io/builder"
)

// CountInconsistentOwnerTeams returns the amount of owner teams that any of
// their access modes not set to the right value. For external trackers and external wikis
// it should be Read and otherwise it should be Owner.
func CountInconsistentOwnerTeams(ctx context.Context) (int64, error) {
cnt1, err := db.GetEngine(ctx).Table("team").
Join("INNER", "team_unit", "`team`.id = `team_unit`.team_id").
Where("`team`.lower_name = ?", strings.ToLower(OwnerTeamName)).
And(builder.Or(builder.Eq{"`team_unit`.`type`": unit.TypeExternalTracker}, builder.Eq{"`team_unit`.`type`": unit.TypeExternalWiki})).
And("`team_unit`.`access_mode` <> ?", perm.AccessModeRead).
GroupBy("`team`.`id`").
Count()
if err != nil {
return 0, err
}

cnt2, err := db.GetEngine(ctx).Table("team").
Join("INNER", "team_unit", "`team`.id = `team_unit`.team_id").
Where("`team`.lower_name = ?", strings.ToLower(OwnerTeamName)).
And(builder.And(builder.Neq{"`team_unit`.`type`": unit.TypeExternalTracker}, builder.Neq{"`team_unit`.`type`": unit.TypeExternalWiki})).
And("`team_unit`.`access_mode` <> ?", perm.AccessModeOwner).
GroupBy("`team_unit`.team_id").
Count()
if err != nil {
return 0, err
}
return cnt1 + cnt2, nil
}

// FixInconsistentOwnerTeams fixes inconsistent owner teams that of
// their access modes not set to the right value. For external trackers and external wikis
// it should be Read and otherwise it should be Owner.
func FixInconsistentOwnerTeams(ctx context.Context) (int64, error) {
subQuery := builder.Select("id").From("team").Where(builder.Eq{"lower_name": strings.ToLower(OwnerTeamName)})
updated1, err := db.GetEngine(ctx).Table("team_unit").
Where(builder.Or(builder.Eq{"`team_unit`.`type`": unit.TypeExternalTracker}, builder.Eq{"`team_unit`.`type`": unit.TypeExternalWiki})).
In("team_id", subQuery).
Cols("access_mode").
Update(&TeamUnit{
AccessMode: perm.AccessModeRead,
})
if err != nil {
return 0, err
}

updated2, err := db.GetEngine(ctx).Table("team_unit").
Where(builder.And(builder.Neq{"`team_unit`.`type`": unit.TypeExternalTracker}, builder.Neq{"`team_unit`.`type`": unit.TypeExternalWiki})).
In("team_id", subQuery).
Cols("access_mode").
Update(&TeamUnit{
AccessMode: perm.AccessModeOwner,
})
if err != nil {
return 0, err
}
return updated1 + updated2, nil
}
51 changes: 26 additions & 25 deletions routers/api/v1/org/team.go
Original file line number Diff line number Diff line change
Expand Up @@ -293,45 +293,46 @@ func EditTeam(ctx *context.APIContext) {
team.CanCreateOrgRepo = team.IsOwnerTeam() || *form.CanCreateOrgRepo
}

teamName := team.Name
if len(form.Name) > 0 {
team.Name = form.Name
teamName = form.Name
}

description := team.Description
if form.Description != nil {
team.Description = *form.Description
description = *form.Description
}

isAuthChanged := false
isIncludeAllChanged := false
if !team.IsOwnerTeam() && len(form.Permission) != 0 {
// Validate permission level.
p := perm.ParseAccessMode(form.Permission)
if p < perm.AccessModeAdmin && len(form.UnitsMap) > 0 {
p = unit_model.MinUnitAccessMode(convertUnitsMap(form.UnitsMap))
}

if team.AccessMode != p {
isAuthChanged = true
team.AccessMode = p
}
includeAllRepos := team.IncludesAllRepositories
if form.IncludesAllRepositories != nil {
includeAllRepos = *form.IncludesAllRepositories
}

if form.IncludesAllRepositories != nil {
isIncludeAllChanged = true
team.IncludesAllRepositories = *form.IncludesAllRepositories
}
canCreateOrgRepo := team.CanCreateOrgRepo
if form.CanCreateOrgRepo != nil {
canCreateOrgRepo = *form.CanCreateOrgRepo
}

unitPerms := make(map[unit_model.Type]perm.AccessMode)
if team.AccessMode < perm.AccessModeAdmin {
if len(form.UnitsMap) > 0 {
attachTeamUnitsMap(team, form.UnitsMap)
} else if len(form.Units) > 0 {
attachTeamUnits(team, form.Units)
unitPerms = convertUnitsMap(form.UnitsMap)
}
} else {
attachAdminTeamUnits(team)
}

if err := org_service.UpdateTeam(ctx, team, isAuthChanged, isIncludeAllChanged); err != nil {
isAdmin := team.AccessMode == perm.AccessModeAdmin
if form.Permission != "" && form.Permission == "admin" {
isAdmin = true
}

if err := org_service.UpdateTeam(ctx, team, org_service.UpdateTeamOptions{
TeamName: teamName,
Description: description,
IsAdmin: isAdmin,
IncludesAllRepositories: includeAllRepos,
CanCreateOrgRepo: canCreateOrgRepo,
UnitPerms: unitPerms,
}); err != nil {
ctx.Error(http.StatusInternalServerError, "EditTeam", err)
return
}
Expand Down
53 changes: 9 additions & 44 deletions routers/web/org/teams.go
Original file line number Diff line number Diff line change
Expand Up @@ -481,61 +481,26 @@ func EditTeam(ctx *context.Context) {
func EditTeamPost(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.CreateTeamForm)
t := ctx.Org.Team
newAccessMode := perm.ParseAccessMode(form.Permission)
unitPerms := getUnitPerms(ctx.Req.Form, newAccessMode)
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
isIncludeAllChanged := false
includesAllRepositories := form.RepoAccess == "all"
unitPerms := getUnitPerms(ctx.Req.Form, perm.ParseAccessMode(form.Permission))

ctx.Data["Title"] = ctx.Org.Organization.FullName
ctx.Data["PageIsOrgTeams"] = true
ctx.Data["Team"] = t
ctx.Data["Units"] = unit_model.Units

if !t.IsOwnerTeam() {
t.Name = form.TeamName
if t.AccessMode != newAccessMode {
isAuthChanged = true
t.AccessMode = newAccessMode
}

if t.IncludesAllRepositories != includesAllRepositories {
isIncludeAllChanged = true
t.IncludesAllRepositories = includesAllRepositories
}
t.CanCreateOrgRepo = form.CanCreateOrgRepo
} else {
t.CanCreateOrgRepo = true
}

t.Description = form.Description
units := make([]*org_model.TeamUnit, 0, len(unitPerms))
for tp, perm := range unitPerms {
units = append(units, &org_model.TeamUnit{
OrgID: t.OrgID,
TeamID: t.ID,
Type: tp,
AccessMode: perm,
})
}
t.Units = units

if ctx.HasError() {
ctx.HTML(http.StatusOK, tplTeamNew)
return
}

if t.AccessMode < perm.AccessModeAdmin && len(unitPerms) == 0 {
ctx.RenderWithErr(ctx.Tr("form.team_no_units_error"), tplTeamNew, &form)
return
}

if err := org_service.UpdateTeam(ctx, t, isAuthChanged, isIncludeAllChanged); err != nil {
if err := org_service.UpdateTeam(ctx, t, org_service.UpdateTeamOptions{
TeamName: form.TeamName,
Description: form.Description,
IsAdmin: form.Permission == "admin",
IncludesAllRepositories: form.RepoAccess == "all",
CanCreateOrgRepo: form.CanCreateOrgRepo,
UnitPerms: unitPerms,
}); err != nil {
ctx.Data["Err_TeamName"] = true
switch {
case org_model.IsErrTeamAlreadyExist(err):
Expand Down
7 changes: 7 additions & 0 deletions services/doctor/dbconsistency.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/migrations"
org_model "code.gitea.io/gitea/models/organization"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
Expand Down Expand Up @@ -164,6 +165,12 @@ func prepareDBConsistencyChecks() []consistencyCheck {
Fixer: repo_model.DeleteOrphanedTopics,
FixedMessage: "Removed",
},
{
Name: "Owner teams with wrong admin access unit",
Counter: org_model.CountInconsistentOwnerTeams,
Fixer: org_model.FixInconsistentOwnerTeams,
FixedMessage: "Fixed",
},
}

// TODO: function to recalc all counters
Expand Down
3 changes: 1 addition & 2 deletions services/doctor/fix8312.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
org_model "code.gitea.io/gitea/models/organization"
"code.gitea.io/gitea/models/perm"
"code.gitea.io/gitea/modules/log"
org_service "code.gitea.io/gitea/services/org"

"xorm.io/builder"
)
Expand All @@ -29,7 +28,7 @@ func fixOwnerTeamCreateOrgRepo(ctx context.Context, logger log.Logger, autofix b
return nil
}

return org_service.UpdateTeam(ctx, team, false, false)
return org_model.UpdateTeam(ctx, team, "can_create_org_repo")
},
)
if err != nil {
Expand Down
11 changes: 2 additions & 9 deletions services/forms/org.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,19 +53,12 @@ func (f *UpdateOrgSettingForm) Validate(req *http.Request, errs binding.Errors)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
}

// ___________
// \__ ___/___ _____ _____
// | |_/ __ \\__ \ / \
// | |\ ___/ / __ \| Y Y \
// |____| \___ >____ /__|_| /
// \/ \/ \/

// CreateTeamForm form for creating team
type CreateTeamForm struct {
TeamName string `binding:"Required;AlphaDashDot;MaxSize(255)"`
Description string `binding:"MaxSize(255)"`
Permission string
RepoAccess string
Permission string `binding:"Required;In(,read,write,admin)"`
RepoAccess string `binding:"Required;In(specified, all)"`
CanCreateOrgRepo bool
}

Expand Down
Loading
Loading