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  }