summaryrefslogtreecommitdiffstats
path: root/models/organization/team.go
diff options
context:
space:
mode:
authorDaniel Baumann <daniel@debian.org>2024-10-18 20:33:49 +0200
committerDaniel Baumann <daniel@debian.org>2024-12-12 23:57:56 +0100
commite68b9d00a6e05b3a941f63ffb696f91e554ac5ec (patch)
tree97775d6c13b0f416af55314eb6a89ef792474615 /models/organization/team.go
parentInitial commit. (diff)
downloadforgejo-e68b9d00a6e05b3a941f63ffb696f91e554ac5ec.tar.xz
forgejo-e68b9d00a6e05b3a941f63ffb696f91e554ac5ec.zip
Adding upstream version 9.0.3.
Signed-off-by: Daniel Baumann <daniel@debian.org>
Diffstat (limited to 'models/organization/team.go')
-rw-r--r--models/organization/team.go310
1 files changed, 310 insertions, 0 deletions
diff --git a/models/organization/team.go b/models/organization/team.go
new file mode 100644
index 0000000..ddff32c
--- /dev/null
+++ b/models/organization/team.go
@@ -0,0 +1,310 @@
+// Copyright 2018 The Gitea Authors. All rights reserved.
+// Copyright 2016 The Gogs Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package organization
+
+import (
+ "context"
+ "fmt"
+ "strings"
+
+ "code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/models/perm"
+ repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/models/unit"
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/util"
+
+ "xorm.io/builder"
+)
+
+// ___________
+// \__ ___/___ _____ _____
+// | |_/ __ \\__ \ / \
+// | |\ ___/ / __ \| Y Y \
+// |____| \___ >____ /__|_| /
+// \/ \/ \/
+
+// ErrTeamAlreadyExist represents a "TeamAlreadyExist" kind of error.
+type ErrTeamAlreadyExist struct {
+ OrgID int64
+ Name string
+}
+
+// IsErrTeamAlreadyExist checks if an error is a ErrTeamAlreadyExist.
+func IsErrTeamAlreadyExist(err error) bool {
+ _, ok := err.(ErrTeamAlreadyExist)
+ return ok
+}
+
+func (err ErrTeamAlreadyExist) Error() string {
+ return fmt.Sprintf("team already exists [org_id: %d, name: %s]", err.OrgID, err.Name)
+}
+
+func (err ErrTeamAlreadyExist) Unwrap() error {
+ return util.ErrAlreadyExist
+}
+
+// ErrTeamNotExist represents a "TeamNotExist" error
+type ErrTeamNotExist struct {
+ OrgID int64
+ TeamID int64
+ Name string
+}
+
+// IsErrTeamNotExist checks if an error is a ErrTeamNotExist.
+func IsErrTeamNotExist(err error) bool {
+ _, ok := err.(ErrTeamNotExist)
+ return ok
+}
+
+func (err ErrTeamNotExist) Error() string {
+ return fmt.Sprintf("team does not exist [org_id %d, team_id %d, name: %s]", err.OrgID, err.TeamID, err.Name)
+}
+
+func (err ErrTeamNotExist) Unwrap() error {
+ return util.ErrNotExist
+}
+
+// OwnerTeamName return the owner team name
+const OwnerTeamName = "Owners"
+
+// Team represents a organization team.
+type Team struct {
+ ID int64 `xorm:"pk autoincr"`
+ OrgID int64 `xorm:"INDEX"`
+ LowerName string
+ Name string
+ Description string
+ AccessMode perm.AccessMode `xorm:"'authorize'"`
+ Repos []*repo_model.Repository `xorm:"-"`
+ Members []*user_model.User `xorm:"-"`
+ NumRepos int
+ NumMembers int
+ Units []*TeamUnit `xorm:"-"`
+ IncludesAllRepositories bool `xorm:"NOT NULL DEFAULT false"`
+ CanCreateOrgRepo bool `xorm:"NOT NULL DEFAULT false"`
+}
+
+func init() {
+ db.RegisterModel(new(Team))
+ db.RegisterModel(new(TeamUser))
+ db.RegisterModel(new(TeamRepo))
+ db.RegisterModel(new(TeamUnit))
+ db.RegisterModel(new(TeamInvite))
+}
+
+func (t *Team) LogString() string {
+ if t == nil {
+ return "<Team nil>"
+ }
+ return fmt.Sprintf("<Team %d:%s OrgID=%d AccessMode=%s>", t.ID, t.Name, t.OrgID, t.AccessMode.LogString())
+}
+
+// LoadUnits load a list of available units for a team
+func (t *Team) LoadUnits(ctx context.Context) (err error) {
+ if t.Units != nil {
+ return nil
+ }
+
+ t.Units, err = getUnitsByTeamID(ctx, t.ID)
+ return err
+}
+
+// GetUnitNames returns the team units names
+func (t *Team) GetUnitNames() (res []string) {
+ if t.AccessMode >= perm.AccessModeAdmin {
+ return unit.AllUnitKeyNames()
+ }
+
+ for _, u := range t.Units {
+ res = append(res, unit.Units[u.Type].NameKey)
+ }
+ return res
+}
+
+// GetUnitsMap returns the team units permissions
+func (t *Team) GetUnitsMap() map[string]string {
+ m := make(map[string]string)
+ if t.AccessMode >= perm.AccessModeAdmin {
+ for _, u := range unit.Units {
+ m[u.NameKey] = t.AccessMode.String()
+ }
+ } else {
+ for _, u := range t.Units {
+ m[u.Unit().NameKey] = u.AccessMode.String()
+ }
+ }
+ return m
+}
+
+// IsOwnerTeam returns true if team is owner team.
+func (t *Team) IsOwnerTeam() bool {
+ return t.Name == OwnerTeamName
+}
+
+// IsMember returns true if given user is a member of team.
+func (t *Team) IsMember(ctx context.Context, userID int64) bool {
+ isMember, err := IsTeamMember(ctx, t.OrgID, t.ID, userID)
+ if err != nil {
+ log.Error("IsMember: %v", err)
+ return false
+ }
+ return isMember
+}
+
+// LoadRepositories returns paginated repositories in team of organization.
+func (t *Team) LoadRepositories(ctx context.Context) (err error) {
+ if t.Repos != nil {
+ return nil
+ }
+ t.Repos, err = GetTeamRepositories(ctx, &SearchTeamRepoOptions{
+ TeamID: t.ID,
+ })
+ return err
+}
+
+// LoadMembers returns paginated members in team of organization.
+func (t *Team) LoadMembers(ctx context.Context) (err error) {
+ t.Members, err = GetTeamMembers(ctx, &SearchMembersOptions{
+ TeamID: t.ID,
+ })
+ return err
+}
+
+// UnitEnabled returns if the team has the given unit type enabled
+func (t *Team) UnitEnabled(ctx context.Context, tp unit.Type) bool {
+ return t.UnitAccessMode(ctx, tp) > perm.AccessModeNone
+}
+
+// UnitAccessMode returns if the team has the given unit type enabled
+func (t *Team) UnitAccessMode(ctx context.Context, tp unit.Type) perm.AccessMode {
+ if err := t.LoadUnits(ctx); err != nil {
+ log.Warn("Error loading team (ID: %d) units: %s", t.ID, err.Error())
+ }
+
+ for _, unit := range t.Units {
+ if unit.Type == tp {
+ return unit.AccessMode
+ }
+ }
+ return perm.AccessModeNone
+}
+
+// IsUsableTeamName tests if a name could be as team name
+func IsUsableTeamName(name string) error {
+ switch name {
+ case "new":
+ return db.ErrNameReserved{Name: name}
+ default:
+ return nil
+ }
+}
+
+// GetTeam returns team by given team name and organization.
+func GetTeam(ctx context.Context, orgID int64, name string) (*Team, error) {
+ t, exist, err := db.Get[Team](ctx, builder.Eq{"org_id": orgID, "lower_name": strings.ToLower(name)})
+ if err != nil {
+ return nil, err
+ } else if !exist {
+ return nil, ErrTeamNotExist{orgID, 0, name}
+ }
+ return t, nil
+}
+
+// GetTeamIDsByNames returns a slice of team ids corresponds to names.
+func GetTeamIDsByNames(ctx context.Context, orgID int64, names []string, ignoreNonExistent bool) ([]int64, error) {
+ ids := make([]int64, 0, len(names))
+ for _, name := range names {
+ u, err := GetTeam(ctx, orgID, name)
+ if err != nil {
+ if ignoreNonExistent {
+ continue
+ }
+ return nil, err
+ }
+ ids = append(ids, u.ID)
+ }
+ return ids, nil
+}
+
+// GetOwnerTeam returns team by given team name and organization.
+func GetOwnerTeam(ctx context.Context, orgID int64) (*Team, error) {
+ return GetTeam(ctx, orgID, OwnerTeamName)
+}
+
+// GetTeamByID returns team by given ID.
+func GetTeamByID(ctx context.Context, teamID int64) (*Team, error) {
+ t := new(Team)
+ has, err := db.GetEngine(ctx).ID(teamID).Get(t)
+ if err != nil {
+ return nil, err
+ } else if !has {
+ return nil, ErrTeamNotExist{0, teamID, ""}
+ }
+ 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
+func IncrTeamRepoNum(ctx context.Context, teamID int64) error {
+ _, err := db.GetEngine(ctx).Incr("num_repos").ID(teamID).Update(new(Team))
+ return err
+}
+
+// CountInconsistentOwnerTeams returns the amount of owner teams that have all of
+// their access modes set to "None".
+func CountInconsistentOwnerTeams(ctx context.Context) (int64, error) {
+ return db.GetEngine(ctx).Table("team").
+ Join("INNER", "team_unit", "`team`.id = `team_unit`.team_id").
+ Where("`team`.lower_name = ?", strings.ToLower(OwnerTeamName)).
+ GroupBy("`team_unit`.team_id").
+ Having("SUM(`team_unit`.access_mode) = 0").
+ Count()
+}
+
+// FixInconsistentOwnerTeams fixes inconsistent owner teams that have all of
+// their access modes set to "None", it sets it back to "Owner".
+func FixInconsistentOwnerTeams(ctx context.Context) (int64, error) {
+ teamIDs := []int64{}
+ if err := db.GetEngine(ctx).Table("team").
+ Select("`team`.id").
+ Join("INNER", "team_unit", "`team`.id = `team_unit`.team_id").
+ Where("`team`.lower_name = ?", strings.ToLower(OwnerTeamName)).
+ GroupBy("`team_unit`.team_id").
+ Having("SUM(`team_unit`.access_mode) = 0").
+ Find(&teamIDs); err != nil {
+ return 0, err
+ }
+
+ if err := db.Iterate(ctx, builder.In("team_id", teamIDs), func(ctx context.Context, bean *TeamUnit) error {
+ if bean.Type == unit.TypeExternalTracker || bean.Type == unit.TypeExternalWiki {
+ bean.AccessMode = perm.AccessModeRead
+ } else {
+ bean.AccessMode = perm.AccessModeOwner
+ }
+ _, err := db.GetEngine(ctx).ID(bean.ID).Table("team_unit").Cols("access_mode").Update(bean)
+ return err
+ }); err != nil {
+ return 0, err
+ }
+
+ return int64(len(teamIDs)), nil
+}