code.gitea.io/gitea@v1.22.3/models/organization/team.go (about) 1 // Copyright 2018 The Gitea Authors. All rights reserved. 2 // Copyright 2016 The Gogs Authors. All rights reserved. 3 // SPDX-License-Identifier: MIT 4 5 package organization 6 7 import ( 8 "context" 9 "fmt" 10 "strings" 11 12 "code.gitea.io/gitea/models/db" 13 "code.gitea.io/gitea/models/perm" 14 repo_model "code.gitea.io/gitea/models/repo" 15 "code.gitea.io/gitea/models/unit" 16 user_model "code.gitea.io/gitea/models/user" 17 "code.gitea.io/gitea/modules/log" 18 "code.gitea.io/gitea/modules/util" 19 20 "xorm.io/builder" 21 ) 22 23 // ___________ 24 // \__ ___/___ _____ _____ 25 // | |_/ __ \\__ \ / \ 26 // | |\ ___/ / __ \| Y Y \ 27 // |____| \___ >____ /__|_| / 28 // \/ \/ \/ 29 30 // ErrTeamAlreadyExist represents a "TeamAlreadyExist" kind of error. 31 type ErrTeamAlreadyExist struct { 32 OrgID int64 33 Name string 34 } 35 36 // IsErrTeamAlreadyExist checks if an error is a ErrTeamAlreadyExist. 37 func IsErrTeamAlreadyExist(err error) bool { 38 _, ok := err.(ErrTeamAlreadyExist) 39 return ok 40 } 41 42 func (err ErrTeamAlreadyExist) Error() string { 43 return fmt.Sprintf("team already exists [org_id: %d, name: %s]", err.OrgID, err.Name) 44 } 45 46 func (err ErrTeamAlreadyExist) Unwrap() error { 47 return util.ErrAlreadyExist 48 } 49 50 // ErrTeamNotExist represents a "TeamNotExist" error 51 type ErrTeamNotExist struct { 52 OrgID int64 53 TeamID int64 54 Name string 55 } 56 57 // IsErrTeamNotExist checks if an error is a ErrTeamNotExist. 58 func IsErrTeamNotExist(err error) bool { 59 _, ok := err.(ErrTeamNotExist) 60 return ok 61 } 62 63 func (err ErrTeamNotExist) Error() string { 64 return fmt.Sprintf("team does not exist [org_id %d, team_id %d, name: %s]", err.OrgID, err.TeamID, err.Name) 65 } 66 67 func (err ErrTeamNotExist) Unwrap() error { 68 return util.ErrNotExist 69 } 70 71 // OwnerTeamName return the owner team name 72 const OwnerTeamName = "Owners" 73 74 // Team represents a organization team. 75 type Team struct { 76 ID int64 `xorm:"pk autoincr"` 77 OrgID int64 `xorm:"INDEX"` 78 LowerName string 79 Name string 80 Description string 81 AccessMode perm.AccessMode `xorm:"'authorize'"` 82 Repos []*repo_model.Repository `xorm:"-"` 83 Members []*user_model.User `xorm:"-"` 84 NumRepos int 85 NumMembers int 86 Units []*TeamUnit `xorm:"-"` 87 IncludesAllRepositories bool `xorm:"NOT NULL DEFAULT false"` 88 CanCreateOrgRepo bool `xorm:"NOT NULL DEFAULT false"` 89 } 90 91 func init() { 92 db.RegisterModel(new(Team)) 93 db.RegisterModel(new(TeamUser)) 94 db.RegisterModel(new(TeamRepo)) 95 db.RegisterModel(new(TeamUnit)) 96 db.RegisterModel(new(TeamInvite)) 97 } 98 99 func (t *Team) LogString() string { 100 if t == nil { 101 return "<Team nil>" 102 } 103 return fmt.Sprintf("<Team %d:%s OrgID=%d AccessMode=%s>", t.ID, t.Name, t.OrgID, t.AccessMode.LogString()) 104 } 105 106 // LoadUnits load a list of available units for a team 107 func (t *Team) LoadUnits(ctx context.Context) (err error) { 108 if t.Units != nil { 109 return nil 110 } 111 112 t.Units, err = getUnitsByTeamID(ctx, t.ID) 113 return err 114 } 115 116 // GetUnitNames returns the team units names 117 func (t *Team) GetUnitNames() (res []string) { 118 if t.AccessMode >= perm.AccessModeAdmin { 119 return unit.AllUnitKeyNames() 120 } 121 122 for _, u := range t.Units { 123 res = append(res, unit.Units[u.Type].NameKey) 124 } 125 return res 126 } 127 128 // GetUnitsMap returns the team units permissions 129 func (t *Team) GetUnitsMap() map[string]string { 130 m := make(map[string]string) 131 if t.AccessMode >= perm.AccessModeAdmin { 132 for _, u := range unit.Units { 133 m[u.NameKey] = t.AccessMode.ToString() 134 } 135 } else { 136 for _, u := range t.Units { 137 m[u.Unit().NameKey] = u.AccessMode.ToString() 138 } 139 } 140 return m 141 } 142 143 // IsOwnerTeam returns true if team is owner team. 144 func (t *Team) IsOwnerTeam() bool { 145 return t.Name == OwnerTeamName 146 } 147 148 // IsMember returns true if given user is a member of team. 149 func (t *Team) IsMember(ctx context.Context, userID int64) bool { 150 isMember, err := IsTeamMember(ctx, t.OrgID, t.ID, userID) 151 if err != nil { 152 log.Error("IsMember: %v", err) 153 return false 154 } 155 return isMember 156 } 157 158 // LoadRepositories returns paginated repositories in team of organization. 159 func (t *Team) LoadRepositories(ctx context.Context) (err error) { 160 if t.Repos != nil { 161 return nil 162 } 163 t.Repos, err = GetTeamRepositories(ctx, &SearchTeamRepoOptions{ 164 TeamID: t.ID, 165 }) 166 return err 167 } 168 169 // LoadMembers returns paginated members in team of organization. 170 func (t *Team) LoadMembers(ctx context.Context) (err error) { 171 t.Members, err = GetTeamMembers(ctx, &SearchMembersOptions{ 172 TeamID: t.ID, 173 }) 174 return err 175 } 176 177 // UnitEnabled returns true if the team has the given unit type enabled 178 func (t *Team) UnitEnabled(ctx context.Context, tp unit.Type) bool { 179 return t.UnitAccessMode(ctx, tp) > perm.AccessModeNone 180 } 181 182 // UnitAccessMode returns the access mode for the given unit type, "none" for non-existent units 183 func (t *Team) UnitAccessMode(ctx context.Context, tp unit.Type) perm.AccessMode { 184 accessMode, _ := t.UnitAccessModeEx(ctx, tp) 185 return accessMode 186 } 187 188 func (t *Team) UnitAccessModeEx(ctx context.Context, tp unit.Type) (accessMode perm.AccessMode, exist bool) { 189 if err := t.LoadUnits(ctx); err != nil { 190 log.Warn("Error loading team (ID: %d) units: %s", t.ID, err.Error()) 191 } 192 for _, u := range t.Units { 193 if u.Type == tp { 194 return u.AccessMode, true 195 } 196 } 197 return perm.AccessModeNone, false 198 } 199 200 // IsUsableTeamName tests if a name could be as team name 201 func IsUsableTeamName(name string) error { 202 switch name { 203 case "new": 204 return db.ErrNameReserved{Name: name} 205 default: 206 return nil 207 } 208 } 209 210 // GetTeam returns team by given team name and organization. 211 func GetTeam(ctx context.Context, orgID int64, name string) (*Team, error) { 212 t, exist, err := db.Get[Team](ctx, builder.Eq{"org_id": orgID, "lower_name": strings.ToLower(name)}) 213 if err != nil { 214 return nil, err 215 } else if !exist { 216 return nil, ErrTeamNotExist{orgID, 0, name} 217 } 218 return t, nil 219 } 220 221 // GetTeamIDsByNames returns a slice of team ids corresponds to names. 222 func GetTeamIDsByNames(ctx context.Context, orgID int64, names []string, ignoreNonExistent bool) ([]int64, error) { 223 ids := make([]int64, 0, len(names)) 224 for _, name := range names { 225 u, err := GetTeam(ctx, orgID, name) 226 if err != nil { 227 if ignoreNonExistent { 228 continue 229 } 230 return nil, err 231 } 232 ids = append(ids, u.ID) 233 } 234 return ids, nil 235 } 236 237 // GetOwnerTeam returns team by given team name and organization. 238 func GetOwnerTeam(ctx context.Context, orgID int64) (*Team, error) { 239 return GetTeam(ctx, orgID, OwnerTeamName) 240 } 241 242 // GetTeamByID returns team by given ID. 243 func GetTeamByID(ctx context.Context, teamID int64) (*Team, error) { 244 t := new(Team) 245 has, err := db.GetEngine(ctx).ID(teamID).Get(t) 246 if err != nil { 247 return nil, err 248 } else if !has { 249 return nil, ErrTeamNotExist{0, teamID, ""} 250 } 251 return t, nil 252 } 253 254 // GetTeamNamesByID returns team's lower name from a list of team ids. 255 func GetTeamNamesByID(ctx context.Context, teamIDs []int64) ([]string, error) { 256 if len(teamIDs) == 0 { 257 return []string{}, nil 258 } 259 260 var teamNames []string 261 err := db.GetEngine(ctx).Table("team"). 262 Select("lower_name"). 263 In("id", teamIDs). 264 Asc("name"). 265 Find(&teamNames) 266 267 return teamNames, err 268 } 269 270 // IncrTeamRepoNum increases the number of repos for the given team by 1 271 func IncrTeamRepoNum(ctx context.Context, teamID int64) error { 272 _, err := db.GetEngine(ctx).Incr("num_repos").ID(teamID).Update(new(Team)) 273 return err 274 }