Files
Gitea/models/organization/team_repo.go
T
bircni ed4a23e893 enhance: inherit team access for all units (#38938)
Admin and write team authorize now grant that mode on every unit,
including units added later, instead of only rows present in
`team_unit`. Granular teams keep `authorize=none` and explicit unit
rows.

Closes the `TEAM-UNIT-PERMISSION` design gap from
https://github.com/go-gitea/gitea/pull/34128.

Maybe also fix #15962 (actually maybe it had been fixed before, the root
cause is out-of-sync "access" table)


## Screenshots

only writing selected:
<img width="1399" height="1007" alt="image"
src="https://github.com/user-attachments/assets/1d1b4c49-a59a-47b6-998f-0464a067395b"
/>


_Created with the help of AI_

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-17 20:30:24 +00:00

97 lines
3.2 KiB
Go

// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package organization
import (
"context"
"gitea.dev/models/db"
"gitea.dev/models/perm"
"gitea.dev/models/unit"
"xorm.io/builder"
)
// TeamRepo represents an team-repository relation.
type TeamRepo struct {
ID int64 `xorm:"pk autoincr"`
OrgID int64 `xorm:"INDEX"`
TeamID int64 `xorm:"UNIQUE(s)"`
RepoID int64 `xorm:"UNIQUE(s)"`
}
// HasTeamRepo returns true if given repository belongs to team.
func HasTeamRepo(ctx context.Context, orgID, teamID, repoID int64) bool {
has, _ := db.GetEngine(ctx).
Where("org_id=?", orgID).
And("team_id=?", teamID).
And("repo_id=?", repoID).
Get(new(TeamRepo))
return has
}
// AddTeamRepo adds a repo for an organization's team
func AddTeamRepo(ctx context.Context, orgID, teamID, repoID int64) error {
_, err := db.GetEngine(ctx).Insert(&TeamRepo{
OrgID: orgID,
TeamID: teamID,
RepoID: repoID,
})
return err
}
// RemoveTeamRepo remove repository from team
func RemoveTeamRepo(ctx context.Context, teamID, repoID int64) error {
_, err := db.DeleteByBean(ctx, &TeamRepo{
TeamID: teamID,
RepoID: repoID,
})
return err
}
// GetTeamsWithAccessToAnyRepoUnit returns all teams in an organization that have given access level to the repository special unit.
// This function is only used for finding some teams that can be used as branch protection allowlist or reviewers, it isn't really used for access control.
func GetTeamsWithAccessToAnyRepoUnit(ctx context.Context, orgID, repoID int64, mode perm.AccessMode, unitType unit.Type, unitTypesMore ...unit.Type) (teams []*Team, err error) {
teamIDs, err := getTeamIDsWithAccessToAnyRepoUnit(ctx, orgID, repoID, mode, unitType, unitTypesMore...)
if err != nil {
return nil, err
}
if len(teamIDs) == 0 {
return teams, nil
}
err = db.GetEngine(ctx).Where(builder.In("id", teamIDs)).OrderBy("team.name").Find(&teams)
return teams, err
}
func getTeamIDsWithAccessToAnyRepoUnit(ctx context.Context, orgID, repoID int64, mode perm.AccessMode, unitType unit.Type, unitTypesMore ...unit.Type) (teamIDs []int64, err error) {
sub := builder.Select("team_id").From("team_unit").
Where(builder.Expr("team_unit.team_id = team.id")).
And(builder.In("team_unit.type", append([]unit.Type{unitType}, unitTypesMore...))).
And(builder.Expr("team_unit.access_mode >= ?", mode))
err = db.GetEngine(ctx).
Select("team.id").
Table("team").
Join("INNER", "team_repo", "team_repo.team_id = team.id").
And("team_repo.org_id = ? AND team_repo.repo_id = ?", orgID, repoID).
And(builder.Or(
builder.Expr("team.authorize >= ?", mode),
builder.In("team.id", sub),
)).
Find(&teamIDs)
return teamIDs, err
}
func GetTeamUserIDsWithAccessToAnyRepoUnit(ctx context.Context, orgID, repoID int64, mode perm.AccessMode, unitType unit.Type, unitTypesMore ...unit.Type) (userIDs []int64, err error) {
teamIDs, err := getTeamIDsWithAccessToAnyRepoUnit(ctx, orgID, repoID, mode, unitType, unitTypesMore...)
if err != nil {
return nil, err
}
if len(teamIDs) == 0 {
return userIDs, nil
}
err = db.GetEngine(ctx).Table("team_user").Select("uid").Where(builder.In("team_id", teamIDs)).Find(&userIDs)
return userIDs, err
}