Skip to content

Commit 4993a36

Browse files
authored
Merge branch 'main' into improve-git-log
2 parents 8b5510c + b7221be commit 4993a36

File tree

6 files changed

+151
-45
lines changed

6 files changed

+151
-45
lines changed

models/migrations/migrations.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,8 @@ var migrations = []Migration{
481481
NewMigration("Change Container Metadata", v1_20.ChangeContainerMetadataMultiArch),
482482
// v251 -> v252
483483
NewMigration("Fix incorrect owner team unit access mode", v1_20.FixIncorrectOwnerTeamUnitAccessMode),
484+
// v252 -> v253
485+
NewMigration("Fix incorrect admin team unit access mode", v1_20.FixIncorrectAdminTeamUnitAccessMode),
484486
}
485487

486488
// GetCurrentDBVersion returns the current db version

models/migrations/v1_20/v252.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright 2023 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package v1_20 //nolint
5+
6+
import (
7+
"code.gitea.io/gitea/modules/log"
8+
9+
"xorm.io/xorm"
10+
)
11+
12+
func FixIncorrectAdminTeamUnitAccessMode(x *xorm.Engine) error {
13+
type UnitType int
14+
type AccessMode int
15+
16+
type TeamUnit struct {
17+
ID int64 `xorm:"pk autoincr"`
18+
OrgID int64 `xorm:"INDEX"`
19+
TeamID int64 `xorm:"UNIQUE(s)"`
20+
Type UnitType `xorm:"UNIQUE(s)"`
21+
AccessMode AccessMode
22+
}
23+
24+
const (
25+
// AccessModeAdmin admin access
26+
AccessModeAdmin = 3
27+
)
28+
29+
sess := x.NewSession()
30+
defer sess.Close()
31+
32+
if err := sess.Begin(); err != nil {
33+
return err
34+
}
35+
36+
count, err := sess.Table("team_unit").
37+
Where("team_id IN (SELECT id FROM team WHERE authorize = ?)", AccessModeAdmin).
38+
Update(&TeamUnit{
39+
AccessMode: AccessModeAdmin,
40+
})
41+
if err != nil {
42+
return err
43+
}
44+
log.Debug("Updated %d admin team unit access mode to belong to admin instead of none", count)
45+
46+
return sess.Commit()
47+
}

routers/api/v1/org/team.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,21 @@ func attachTeamUnitsMap(team *organization.Team, unitsMap map[string]string) {
166166
}
167167
}
168168

169+
func attachAdminTeamUnits(team *organization.Team) {
170+
team.Units = make([]*organization.TeamUnit, 0, len(unit_model.AllRepoUnitTypes))
171+
for _, ut := range unit_model.AllRepoUnitTypes {
172+
up := perm.AccessModeAdmin
173+
if ut == unit_model.TypeExternalTracker || ut == unit_model.TypeExternalWiki {
174+
up = perm.AccessModeRead
175+
}
176+
team.Units = append(team.Units, &organization.TeamUnit{
177+
OrgID: team.OrgID,
178+
Type: ut,
179+
AccessMode: up,
180+
})
181+
}
182+
}
183+
169184
// CreateTeam api for create a team
170185
func CreateTeam(ctx *context.APIContext) {
171186
// swagger:operation POST /orgs/{org}/teams organization orgCreateTeam
@@ -213,6 +228,8 @@ func CreateTeam(ctx *context.APIContext) {
213228
ctx.Error(http.StatusInternalServerError, "getTeamUnits", errors.New("units permission should not be empty"))
214229
return
215230
}
231+
} else {
232+
attachAdminTeamUnits(team)
216233
}
217234

218235
if err := models.NewTeam(team); err != nil {
@@ -300,6 +317,8 @@ func EditTeam(ctx *context.APIContext) {
300317
} else if len(form.Units) > 0 {
301318
attachTeamUnits(team, form.Units)
302319
}
320+
} else {
321+
attachAdminTeamUnits(team)
303322
}
304323

305324
if err := models.UpdateTeam(team, isAuthChanged, isIncludeAllChanged); err != nil {

routers/web/org/teams.go

Lines changed: 50 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package org
66

77
import (
8+
"fmt"
89
"net/http"
910
"net/url"
1011
"path"
@@ -264,14 +265,26 @@ func NewTeam(ctx *context.Context) {
264265
ctx.HTML(http.StatusOK, tplTeamNew)
265266
}
266267

267-
func getUnitPerms(forms url.Values) map[unit_model.Type]perm.AccessMode {
268+
func getUnitPerms(forms url.Values, teamPermission perm.AccessMode) map[unit_model.Type]perm.AccessMode {
268269
unitPerms := make(map[unit_model.Type]perm.AccessMode)
269-
for k, v := range forms {
270-
if strings.HasPrefix(k, "unit_") {
271-
t, _ := strconv.Atoi(k[5:])
272-
if t > 0 {
273-
vv, _ := strconv.Atoi(v[0])
274-
unitPerms[unit_model.Type(t)] = perm.AccessMode(vv)
270+
for _, ut := range unit_model.AllRepoUnitTypes {
271+
// Default accessmode is none
272+
unitPerms[ut] = perm.AccessModeNone
273+
274+
v, ok := forms[fmt.Sprintf("unit_%d", ut)]
275+
if ok {
276+
vv, _ := strconv.Atoi(v[0])
277+
if teamPermission >= perm.AccessModeAdmin {
278+
unitPerms[ut] = teamPermission
279+
// Don't allow `TypeExternal{Tracker,Wiki}` to influence this as they can only be set to READ perms.
280+
if ut == unit_model.TypeExternalTracker || ut == unit_model.TypeExternalWiki {
281+
unitPerms[ut] = perm.AccessModeRead
282+
}
283+
} else {
284+
unitPerms[ut] = perm.AccessMode(vv)
285+
if unitPerms[ut] >= perm.AccessModeAdmin {
286+
unitPerms[ut] = perm.AccessModeWrite
287+
}
275288
}
276289
}
277290
}
@@ -282,8 +295,8 @@ func getUnitPerms(forms url.Values) map[unit_model.Type]perm.AccessMode {
282295
func NewTeamPost(ctx *context.Context) {
283296
form := web.GetForm(ctx).(*forms.CreateTeamForm)
284297
includesAllRepositories := form.RepoAccess == "all"
285-
unitPerms := getUnitPerms(ctx.Req.Form)
286298
p := perm.ParseAccessMode(form.Permission)
299+
unitPerms := getUnitPerms(ctx.Req.Form, p)
287300
if p < perm.AccessModeAdmin {
288301
// if p is less than admin accessmode, then it should be general accessmode,
289302
// so we should calculate the minial accessmode from units accessmodes.
@@ -299,17 +312,15 @@ func NewTeamPost(ctx *context.Context) {
299312
CanCreateOrgRepo: form.CanCreateOrgRepo,
300313
}
301314

302-
if t.AccessMode < perm.AccessModeAdmin {
303-
units := make([]*org_model.TeamUnit, 0, len(unitPerms))
304-
for tp, perm := range unitPerms {
305-
units = append(units, &org_model.TeamUnit{
306-
OrgID: ctx.Org.Organization.ID,
307-
Type: tp,
308-
AccessMode: perm,
309-
})
310-
}
311-
t.Units = units
315+
units := make([]*org_model.TeamUnit, 0, len(unitPerms))
316+
for tp, perm := range unitPerms {
317+
units = append(units, &org_model.TeamUnit{
318+
OrgID: ctx.Org.Organization.ID,
319+
Type: tp,
320+
AccessMode: perm,
321+
})
312322
}
323+
t.Units = units
313324

314325
ctx.Data["Title"] = ctx.Org.Organization.FullName
315326
ctx.Data["PageIsOrgTeams"] = true
@@ -422,8 +433,11 @@ func SearchTeam(ctx *context.Context) {
422433
func EditTeam(ctx *context.Context) {
423434
ctx.Data["Title"] = ctx.Org.Organization.FullName
424435
ctx.Data["PageIsOrgTeams"] = true
425-
ctx.Data["team_name"] = ctx.Org.Team.Name
426-
ctx.Data["desc"] = ctx.Org.Team.Description
436+
if err := ctx.Org.Team.LoadUnits(ctx); err != nil {
437+
ctx.ServerError("LoadUnits", err)
438+
return
439+
}
440+
ctx.Data["Team"] = ctx.Org.Team
427441
ctx.Data["Units"] = unit_model.Units
428442
ctx.HTML(http.StatusOK, tplTeamNew)
429443
}
@@ -432,7 +446,13 @@ func EditTeam(ctx *context.Context) {
432446
func EditTeamPost(ctx *context.Context) {
433447
form := web.GetForm(ctx).(*forms.CreateTeamForm)
434448
t := ctx.Org.Team
435-
unitPerms := getUnitPerms(ctx.Req.Form)
449+
newAccessMode := perm.ParseAccessMode(form.Permission)
450+
unitPerms := getUnitPerms(ctx.Req.Form, newAccessMode)
451+
if newAccessMode < perm.AccessModeAdmin {
452+
// if newAccessMode is less than admin accessmode, then it should be general accessmode,
453+
// so we should calculate the minial accessmode from units accessmodes.
454+
newAccessMode = unit_model.MinUnitAccessMode(unitPerms)
455+
}
436456
isAuthChanged := false
437457
isIncludeAllChanged := false
438458
includesAllRepositories := form.RepoAccess == "all"
@@ -443,14 +463,6 @@ func EditTeamPost(ctx *context.Context) {
443463
ctx.Data["Units"] = unit_model.Units
444464

445465
if !t.IsOwnerTeam() {
446-
// Validate permission level.
447-
newAccessMode := perm.ParseAccessMode(form.Permission)
448-
if newAccessMode < perm.AccessModeAdmin {
449-
// if p is less than admin accessmode, then it should be general accessmode,
450-
// so we should calculate the minial accessmode from units accessmodes.
451-
newAccessMode = unit_model.MinUnitAccessMode(unitPerms)
452-
}
453-
454466
t.Name = form.TeamName
455467
if t.AccessMode != newAccessMode {
456468
isAuthChanged = true
@@ -467,21 +479,16 @@ func EditTeamPost(ctx *context.Context) {
467479
}
468480

469481
t.Description = form.Description
470-
if t.AccessMode < perm.AccessModeAdmin {
471-
units := make([]org_model.TeamUnit, 0, len(unitPerms))
472-
for tp, perm := range unitPerms {
473-
units = append(units, org_model.TeamUnit{
474-
OrgID: t.OrgID,
475-
TeamID: t.ID,
476-
Type: tp,
477-
AccessMode: perm,
478-
})
479-
}
480-
if err := org_model.UpdateTeamUnits(t, units); err != nil {
481-
ctx.Error(http.StatusInternalServerError, "UpdateTeamUnits", err.Error())
482-
return
483-
}
482+
units := make([]*org_model.TeamUnit, 0, len(unitPerms))
483+
for tp, perm := range unitPerms {
484+
units = append(units, &org_model.TeamUnit{
485+
OrgID: t.OrgID,
486+
TeamID: t.ID,
487+
Type: tp,
488+
AccessMode: perm,
489+
})
484490
}
491+
t.Units = units
485492

486493
if ctx.HasError() {
487494
ctx.HTML(http.StatusOK, tplTeamNew)

templates/org/team/new.tmpl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@
109109
</td>
110110
<td class="center aligned">
111111
<div class="ui radio checkbox">
112-
<input type="radio" name="unit_{{$unit.Type.Value}}" value="2"{{if (eq ($.Team.UnitAccessMode $.Context $unit.Type) 2)}} checked{{end}} {{if $unit.Type.UnitGlobalDisabled}}disabled{{end}} title="{{$.locale.Tr "org.teams.write_access"}}">
112+
<input type="radio" name="unit_{{$unit.Type.Value}}" value="2"{{if (ge ($.Team.UnitAccessMode $.Context $unit.Type) 2)}} checked{{end}} {{if $unit.Type.UnitGlobalDisabled}}disabled{{end}} title="{{$.locale.Tr "org.teams.write_access"}}">
113113
</div>
114114
</td>
115115
</tr>
@@ -137,7 +137,7 @@
137137
{{else}}
138138
<button class="ui green button">{{.locale.Tr "org.teams.update_settings"}}</button>
139139
{{if not (eq .Team.LowerName "owners")}}
140-
<button class="ui red button delete-button" data-url="{{.OrgLink}}/teams/{{.team_name | PathEscape}}/delete">{{.locale.Tr "org.teams.delete_team"}}</button>
140+
<button class="ui red button delete-button" data-url="{{.OrgLink}}/teams/{{.Team.Name | PathEscape}}/delete">{{.locale.Tr "org.teams.delete_team"}}</button>
141141
{{end}}
142142
{{end}}
143143
</div>

tests/integration/api_team_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
auth_model "code.gitea.io/gitea/models/auth"
1313
"code.gitea.io/gitea/models/db"
1414
"code.gitea.io/gitea/models/organization"
15+
"code.gitea.io/gitea/models/perm"
1516
"code.gitea.io/gitea/models/repo"
1617
"code.gitea.io/gitea/models/unit"
1718
"code.gitea.io/gitea/models/unittest"
@@ -189,6 +190,36 @@ func TestAPITeam(t *testing.T) {
189190
req = NewRequestf(t, "DELETE", "/api/v1/teams/%d?token="+token, teamID)
190191
MakeRequest(t, req, http.StatusNoContent)
191192
unittest.AssertNotExistsBean(t, &organization.Team{ID: teamID})
193+
194+
// Create admin team
195+
teamToCreate = &api.CreateTeamOption{
196+
Name: "teamadmin",
197+
Description: "team admin",
198+
IncludesAllRepositories: true,
199+
Permission: "admin",
200+
}
201+
req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/orgs/%s/teams?token=%s", org.Name, token), teamToCreate)
202+
resp = MakeRequest(t, req, http.StatusCreated)
203+
apiTeam = api.Team{}
204+
DecodeJSON(t, resp, &apiTeam)
205+
for _, ut := range unit.AllRepoUnitTypes {
206+
up := perm.AccessModeAdmin
207+
if ut == unit.TypeExternalTracker || ut == unit.TypeExternalWiki {
208+
up = perm.AccessModeRead
209+
}
210+
unittest.AssertExistsAndLoadBean(t, &organization.TeamUnit{
211+
OrgID: org.ID,
212+
TeamID: apiTeam.ID,
213+
Type: ut,
214+
AccessMode: up,
215+
})
216+
}
217+
teamID = apiTeam.ID
218+
219+
// Delete team.
220+
req = NewRequestf(t, "DELETE", "/api/v1/teams/%d?token="+token, teamID)
221+
MakeRequest(t, req, http.StatusNoContent)
222+
unittest.AssertNotExistsBean(t, &organization.Team{ID: teamID})
192223
}
193224

194225
func checkTeamResponse(t *testing.T, testName string, apiTeam *api.Team, name, description string, includesAllRepositories bool, permission string, units []string, unitsMap map[string]string) {

0 commit comments

Comments
 (0)