code.gitea.io/gitea@v1.22.3/models/organization/org.go (about)

     1  // Copyright 2014 The Gogs Authors. All rights reserved.
     2  // Copyright 2019 The Gitea Authors. All rights reserved.
     3  // SPDX-License-Identifier: MIT
     4  
     5  package organization
     6  
     7  import (
     8  	"context"
     9  	"fmt"
    10  	"strings"
    11  
    12  	actions_model "code.gitea.io/gitea/models/actions"
    13  	"code.gitea.io/gitea/models/db"
    14  	"code.gitea.io/gitea/models/perm"
    15  	repo_model "code.gitea.io/gitea/models/repo"
    16  	secret_model "code.gitea.io/gitea/models/secret"
    17  	"code.gitea.io/gitea/models/unit"
    18  	user_model "code.gitea.io/gitea/models/user"
    19  	"code.gitea.io/gitea/modules/log"
    20  	"code.gitea.io/gitea/modules/setting"
    21  	"code.gitea.io/gitea/modules/structs"
    22  	"code.gitea.io/gitea/modules/util"
    23  
    24  	"xorm.io/builder"
    25  )
    26  
    27  // ________                            .__                __  .__
    28  // \_____  \_______  _________    ____ |__|____________ _/  |_|__| ____   ____
    29  //  /   |   \_  __ \/ ___\__  \  /    \|  \___   /\__  \\   __\  |/  _ \ /    \
    30  // /    |    \  | \/ /_/  > __ \|   |  \  |/    /  / __ \|  | |  (  <_> )   |  \
    31  // \_______  /__|  \___  (____  /___|  /__/_____ \(____  /__| |__|\____/|___|  /
    32  //         \/     /_____/     \/     \/         \/     \/                    \/
    33  
    34  // ErrOrgNotExist represents a "OrgNotExist" kind of error.
    35  type ErrOrgNotExist struct {
    36  	ID   int64
    37  	Name string
    38  }
    39  
    40  // IsErrOrgNotExist checks if an error is a ErrOrgNotExist.
    41  func IsErrOrgNotExist(err error) bool {
    42  	_, ok := err.(ErrOrgNotExist)
    43  	return ok
    44  }
    45  
    46  func (err ErrOrgNotExist) Error() string {
    47  	return fmt.Sprintf("org does not exist [id: %d, name: %s]", err.ID, err.Name)
    48  }
    49  
    50  func (err ErrOrgNotExist) Unwrap() error {
    51  	return util.ErrNotExist
    52  }
    53  
    54  // ErrLastOrgOwner represents a "LastOrgOwner" kind of error.
    55  type ErrLastOrgOwner struct {
    56  	UID int64
    57  }
    58  
    59  // IsErrLastOrgOwner checks if an error is a ErrLastOrgOwner.
    60  func IsErrLastOrgOwner(err error) bool {
    61  	_, ok := err.(ErrLastOrgOwner)
    62  	return ok
    63  }
    64  
    65  func (err ErrLastOrgOwner) Error() string {
    66  	return fmt.Sprintf("user is the last member of owner team [uid: %d]", err.UID)
    67  }
    68  
    69  // ErrUserNotAllowedCreateOrg represents a "UserNotAllowedCreateOrg" kind of error.
    70  type ErrUserNotAllowedCreateOrg struct{}
    71  
    72  // IsErrUserNotAllowedCreateOrg checks if an error is an ErrUserNotAllowedCreateOrg.
    73  func IsErrUserNotAllowedCreateOrg(err error) bool {
    74  	_, ok := err.(ErrUserNotAllowedCreateOrg)
    75  	return ok
    76  }
    77  
    78  func (err ErrUserNotAllowedCreateOrg) Error() string {
    79  	return "user is not allowed to create organizations"
    80  }
    81  
    82  func (err ErrUserNotAllowedCreateOrg) Unwrap() error {
    83  	return util.ErrPermissionDenied
    84  }
    85  
    86  // Organization represents an organization
    87  type Organization user_model.User
    88  
    89  // OrgFromUser converts user to organization
    90  func OrgFromUser(user *user_model.User) *Organization {
    91  	return (*Organization)(user)
    92  }
    93  
    94  // TableName represents the real table name of Organization
    95  func (Organization) TableName() string {
    96  	return "user"
    97  }
    98  
    99  // IsOwnedBy returns true if given user is in the owner team.
   100  func (org *Organization) IsOwnedBy(ctx context.Context, uid int64) (bool, error) {
   101  	return IsOrganizationOwner(ctx, org.ID, uid)
   102  }
   103  
   104  // IsOrgAdmin returns true if given user is in the owner team or an admin team.
   105  func (org *Organization) IsOrgAdmin(ctx context.Context, uid int64) (bool, error) {
   106  	return IsOrganizationAdmin(ctx, org.ID, uid)
   107  }
   108  
   109  // IsOrgMember returns true if given user is member of organization.
   110  func (org *Organization) IsOrgMember(ctx context.Context, uid int64) (bool, error) {
   111  	return IsOrganizationMember(ctx, org.ID, uid)
   112  }
   113  
   114  // CanCreateOrgRepo returns true if given user can create repo in organization
   115  func (org *Organization) CanCreateOrgRepo(ctx context.Context, uid int64) (bool, error) {
   116  	return CanCreateOrgRepo(ctx, org.ID, uid)
   117  }
   118  
   119  // GetTeam returns named team of organization.
   120  func (org *Organization) GetTeam(ctx context.Context, name string) (*Team, error) {
   121  	return GetTeam(ctx, org.ID, name)
   122  }
   123  
   124  // GetOwnerTeam returns owner team of organization.
   125  func (org *Organization) GetOwnerTeam(ctx context.Context) (*Team, error) {
   126  	return org.GetTeam(ctx, OwnerTeamName)
   127  }
   128  
   129  // FindOrgTeams returns all teams of a given organization
   130  func FindOrgTeams(ctx context.Context, orgID int64) ([]*Team, error) {
   131  	var teams []*Team
   132  	return teams, db.GetEngine(ctx).
   133  		Where("org_id=?", orgID).
   134  		OrderBy("CASE WHEN name LIKE '" + OwnerTeamName + "' THEN '' ELSE name END").
   135  		Find(&teams)
   136  }
   137  
   138  // LoadTeams load teams if not loaded.
   139  func (org *Organization) LoadTeams(ctx context.Context) ([]*Team, error) {
   140  	return FindOrgTeams(ctx, org.ID)
   141  }
   142  
   143  // GetMembers returns all members of organization.
   144  func (org *Organization) GetMembers(ctx context.Context) (user_model.UserList, map[int64]bool, error) {
   145  	return FindOrgMembers(ctx, &FindOrgMembersOpts{
   146  		OrgID: org.ID,
   147  	})
   148  }
   149  
   150  // HasMemberWithUserID returns true if user with userID is part of the u organisation.
   151  func (org *Organization) HasMemberWithUserID(ctx context.Context, userID int64) bool {
   152  	return org.hasMemberWithUserID(ctx, userID)
   153  }
   154  
   155  func (org *Organization) hasMemberWithUserID(ctx context.Context, userID int64) bool {
   156  	isMember, err := IsOrganizationMember(ctx, org.ID, userID)
   157  	if err != nil {
   158  		log.Error("IsOrganizationMember: %v", err)
   159  		return false
   160  	}
   161  	return isMember
   162  }
   163  
   164  // AvatarLink returns the full avatar link with http host
   165  func (org *Organization) AvatarLink(ctx context.Context) string {
   166  	return org.AsUser().AvatarLink(ctx)
   167  }
   168  
   169  // HTMLURL returns the organization's full link.
   170  func (org *Organization) HTMLURL() string {
   171  	return org.AsUser().HTMLURL()
   172  }
   173  
   174  // OrganisationLink returns the organization sub page link.
   175  func (org *Organization) OrganisationLink() string {
   176  	return org.AsUser().OrganisationLink()
   177  }
   178  
   179  // ShortName ellipses username to length
   180  func (org *Organization) ShortName(length int) string {
   181  	return org.AsUser().ShortName(length)
   182  }
   183  
   184  // HomeLink returns the user or organization home page link.
   185  func (org *Organization) HomeLink() string {
   186  	return org.AsUser().HomeLink()
   187  }
   188  
   189  // CanCreateRepo returns if user login can create a repository
   190  // NOTE: functions calling this assume a failure due to repository count limit; if new checks are added, those functions should be revised
   191  func (org *Organization) CanCreateRepo() bool {
   192  	return org.AsUser().CanCreateRepo()
   193  }
   194  
   195  // FindOrgMembersOpts represensts find org members conditions
   196  type FindOrgMembersOpts struct {
   197  	db.ListOptions
   198  	OrgID      int64
   199  	PublicOnly bool
   200  }
   201  
   202  // CountOrgMembers counts the organization's members
   203  func CountOrgMembers(ctx context.Context, opts *FindOrgMembersOpts) (int64, error) {
   204  	sess := db.GetEngine(ctx).Where("org_id=?", opts.OrgID)
   205  	if opts.PublicOnly {
   206  		sess.And("is_public = ?", true)
   207  	}
   208  	return sess.Count(new(OrgUser))
   209  }
   210  
   211  // FindOrgMembers loads organization members according conditions
   212  func FindOrgMembers(ctx context.Context, opts *FindOrgMembersOpts) (user_model.UserList, map[int64]bool, error) {
   213  	ous, err := GetOrgUsersByOrgID(ctx, opts)
   214  	if err != nil {
   215  		return nil, nil, err
   216  	}
   217  
   218  	ids := make([]int64, len(ous))
   219  	idsIsPublic := make(map[int64]bool, len(ous))
   220  	for i, ou := range ous {
   221  		ids[i] = ou.UID
   222  		idsIsPublic[ou.UID] = ou.IsPublic
   223  	}
   224  
   225  	users, err := user_model.GetUsersByIDs(ctx, ids)
   226  	if err != nil {
   227  		return nil, nil, err
   228  	}
   229  	return users, idsIsPublic, nil
   230  }
   231  
   232  // AsUser returns the org as user object
   233  func (org *Organization) AsUser() *user_model.User {
   234  	return (*user_model.User)(org)
   235  }
   236  
   237  // DisplayName returns full name if it's not empty,
   238  // returns username otherwise.
   239  func (org *Organization) DisplayName() string {
   240  	return org.AsUser().DisplayName()
   241  }
   242  
   243  // CustomAvatarRelativePath returns user custom avatar relative path.
   244  func (org *Organization) CustomAvatarRelativePath() string {
   245  	return org.Avatar
   246  }
   247  
   248  // UnitPermission returns unit permission
   249  func (org *Organization) UnitPermission(ctx context.Context, doer *user_model.User, unitType unit.Type) perm.AccessMode {
   250  	if doer != nil {
   251  		teams, err := GetUserOrgTeams(ctx, org.ID, doer.ID)
   252  		if err != nil {
   253  			log.Error("GetUserOrgTeams: %v", err)
   254  			return perm.AccessModeNone
   255  		}
   256  
   257  		if err := teams.LoadUnits(ctx); err != nil {
   258  			log.Error("LoadUnits: %v", err)
   259  			return perm.AccessModeNone
   260  		}
   261  
   262  		if len(teams) > 0 {
   263  			return teams.UnitMaxAccess(unitType)
   264  		}
   265  	}
   266  
   267  	if org.Visibility.IsPublic() {
   268  		return perm.AccessModeRead
   269  	}
   270  
   271  	return perm.AccessModeNone
   272  }
   273  
   274  // CreateOrganization creates record of a new organization.
   275  func CreateOrganization(ctx context.Context, org *Organization, owner *user_model.User) (err error) {
   276  	if !owner.CanCreateOrganization() {
   277  		return ErrUserNotAllowedCreateOrg{}
   278  	}
   279  
   280  	if err = user_model.IsUsableUsername(org.Name); err != nil {
   281  		return err
   282  	}
   283  
   284  	isExist, err := user_model.IsUserExist(ctx, 0, org.Name)
   285  	if err != nil {
   286  		return err
   287  	} else if isExist {
   288  		return user_model.ErrUserAlreadyExist{Name: org.Name}
   289  	}
   290  
   291  	org.LowerName = strings.ToLower(org.Name)
   292  	if org.Rands, err = user_model.GetUserSalt(); err != nil {
   293  		return err
   294  	}
   295  	if org.Salt, err = user_model.GetUserSalt(); err != nil {
   296  		return err
   297  	}
   298  	org.UseCustomAvatar = true
   299  	org.MaxRepoCreation = -1
   300  	org.NumTeams = 1
   301  	org.NumMembers = 1
   302  	org.Type = user_model.UserTypeOrganization
   303  
   304  	ctx, committer, err := db.TxContext(ctx)
   305  	if err != nil {
   306  		return err
   307  	}
   308  	defer committer.Close()
   309  
   310  	if err = user_model.DeleteUserRedirect(ctx, org.Name); err != nil {
   311  		return err
   312  	}
   313  
   314  	if err = db.Insert(ctx, org); err != nil {
   315  		return fmt.Errorf("insert organization: %w", err)
   316  	}
   317  	if err = user_model.GenerateRandomAvatar(ctx, org.AsUser()); err != nil {
   318  		return fmt.Errorf("generate random avatar: %w", err)
   319  	}
   320  
   321  	// Add initial creator to organization and owner team.
   322  	if err = db.Insert(ctx, &OrgUser{
   323  		UID:      owner.ID,
   324  		OrgID:    org.ID,
   325  		IsPublic: setting.Service.DefaultOrgMemberVisible,
   326  	}); err != nil {
   327  		return fmt.Errorf("insert org-user relation: %w", err)
   328  	}
   329  
   330  	// Create default owner team.
   331  	t := &Team{
   332  		OrgID:                   org.ID,
   333  		LowerName:               strings.ToLower(OwnerTeamName),
   334  		Name:                    OwnerTeamName,
   335  		AccessMode:              perm.AccessModeOwner,
   336  		NumMembers:              1,
   337  		IncludesAllRepositories: true,
   338  		CanCreateOrgRepo:        true,
   339  	}
   340  	if err = db.Insert(ctx, t); err != nil {
   341  		return fmt.Errorf("insert owner team: %w", err)
   342  	}
   343  
   344  	// insert units for team
   345  	units := make([]TeamUnit, 0, len(unit.AllRepoUnitTypes))
   346  	for _, tp := range unit.AllRepoUnitTypes {
   347  		up := perm.AccessModeOwner
   348  		if tp == unit.TypeExternalTracker || tp == unit.TypeExternalWiki {
   349  			up = perm.AccessModeRead
   350  		}
   351  		units = append(units, TeamUnit{
   352  			OrgID:      org.ID,
   353  			TeamID:     t.ID,
   354  			Type:       tp,
   355  			AccessMode: up,
   356  		})
   357  	}
   358  
   359  	if err = db.Insert(ctx, &units); err != nil {
   360  		return err
   361  	}
   362  
   363  	if err = db.Insert(ctx, &TeamUser{
   364  		UID:    owner.ID,
   365  		OrgID:  org.ID,
   366  		TeamID: t.ID,
   367  	}); err != nil {
   368  		return fmt.Errorf("insert team-user relation: %w", err)
   369  	}
   370  
   371  	return committer.Commit()
   372  }
   373  
   374  // GetOrgByName returns organization by given name.
   375  func GetOrgByName(ctx context.Context, name string) (*Organization, error) {
   376  	if len(name) == 0 {
   377  		return nil, ErrOrgNotExist{0, name}
   378  	}
   379  	u := &Organization{
   380  		LowerName: strings.ToLower(name),
   381  		Type:      user_model.UserTypeOrganization,
   382  	}
   383  	has, err := db.GetEngine(ctx).Get(u)
   384  	if err != nil {
   385  		return nil, err
   386  	} else if !has {
   387  		return nil, ErrOrgNotExist{0, name}
   388  	}
   389  	return u, nil
   390  }
   391  
   392  // DeleteOrganization deletes models associated to an organization.
   393  func DeleteOrganization(ctx context.Context, org *Organization) error {
   394  	if org.Type != user_model.UserTypeOrganization {
   395  		return fmt.Errorf("%s is a user not an organization", org.Name)
   396  	}
   397  
   398  	if err := db.DeleteBeans(ctx,
   399  		&Team{OrgID: org.ID},
   400  		&OrgUser{OrgID: org.ID},
   401  		&TeamUser{OrgID: org.ID},
   402  		&TeamUnit{OrgID: org.ID},
   403  		&TeamInvite{OrgID: org.ID},
   404  		&secret_model.Secret{OwnerID: org.ID},
   405  		&user_model.Blocking{BlockerID: org.ID},
   406  		&actions_model.ActionRunner{OwnerID: org.ID},
   407  		&actions_model.ActionRunnerToken{OwnerID: org.ID},
   408  	); err != nil {
   409  		return fmt.Errorf("DeleteBeans: %w", err)
   410  	}
   411  
   412  	if _, err := db.GetEngine(ctx).ID(org.ID).Delete(new(user_model.User)); err != nil {
   413  		return fmt.Errorf("Delete: %w", err)
   414  	}
   415  
   416  	return nil
   417  }
   418  
   419  // GetOrgUserMaxAuthorizeLevel returns highest authorize level of user in an organization
   420  func (org *Organization) GetOrgUserMaxAuthorizeLevel(ctx context.Context, uid int64) (perm.AccessMode, error) {
   421  	var authorize perm.AccessMode
   422  	_, err := db.GetEngine(ctx).
   423  		Select("max(team.authorize)").
   424  		Table("team").
   425  		Join("INNER", "team_user", "team_user.team_id = team.id").
   426  		Where("team_user.uid = ?", uid).
   427  		And("team_user.org_id = ?", org.ID).
   428  		Get(&authorize)
   429  	return authorize, err
   430  }
   431  
   432  // GetUsersWhoCanCreateOrgRepo returns users which are able to create repo in organization
   433  func GetUsersWhoCanCreateOrgRepo(ctx context.Context, orgID int64) (map[int64]*user_model.User, error) {
   434  	// Use a map, in order to de-duplicate users.
   435  	users := make(map[int64]*user_model.User)
   436  	return users, db.GetEngine(ctx).
   437  		Join("INNER", "`team_user`", "`team_user`.uid=`user`.id").
   438  		Join("INNER", "`team`", "`team`.id=`team_user`.team_id").
   439  		Where(builder.Eq{"team.can_create_org_repo": true}.Or(builder.Eq{"team.authorize": perm.AccessModeOwner})).
   440  		And("team_user.org_id = ?", orgID).Find(&users)
   441  }
   442  
   443  // SearchOrganizationsOptions options to filter organizations
   444  type SearchOrganizationsOptions struct {
   445  	db.ListOptions
   446  	All bool
   447  }
   448  
   449  // FindOrgOptions finds orgs options
   450  type FindOrgOptions struct {
   451  	db.ListOptions
   452  	UserID         int64
   453  	IncludePrivate bool
   454  }
   455  
   456  func queryUserOrgIDs(userID int64, includePrivate bool) *builder.Builder {
   457  	cond := builder.Eq{"uid": userID}
   458  	if !includePrivate {
   459  		cond["is_public"] = true
   460  	}
   461  	return builder.Select("org_id").From("org_user").Where(cond)
   462  }
   463  
   464  func (opts FindOrgOptions) ToConds() builder.Cond {
   465  	var cond builder.Cond = builder.Eq{"`user`.`type`": user_model.UserTypeOrganization}
   466  	if opts.UserID > 0 {
   467  		cond = cond.And(builder.In("`user`.`id`", queryUserOrgIDs(opts.UserID, opts.IncludePrivate)))
   468  	}
   469  	if !opts.IncludePrivate {
   470  		cond = cond.And(builder.Eq{"`user`.visibility": structs.VisibleTypePublic})
   471  	}
   472  	return cond
   473  }
   474  
   475  func (opts FindOrgOptions) ToOrders() string {
   476  	return "`user`.name ASC"
   477  }
   478  
   479  // HasOrgOrUserVisible tells if the given user can see the given org or user
   480  func HasOrgOrUserVisible(ctx context.Context, orgOrUser, user *user_model.User) bool {
   481  	// If user is nil, it's an anonymous user/request.
   482  	// The Ghost user is handled like an anonymous user.
   483  	if user == nil || user.IsGhost() {
   484  		return orgOrUser.Visibility == structs.VisibleTypePublic
   485  	}
   486  
   487  	if user.IsAdmin || orgOrUser.ID == user.ID {
   488  		return true
   489  	}
   490  
   491  	if (orgOrUser.Visibility == structs.VisibleTypePrivate || user.IsRestricted) && !OrgFromUser(orgOrUser).hasMemberWithUserID(ctx, user.ID) {
   492  		return false
   493  	}
   494  	return true
   495  }
   496  
   497  // HasOrgsVisible tells if the given user can see at least one of the orgs provided
   498  func HasOrgsVisible(ctx context.Context, orgs []*Organization, user *user_model.User) bool {
   499  	if len(orgs) == 0 {
   500  		return false
   501  	}
   502  
   503  	for _, org := range orgs {
   504  		if HasOrgOrUserVisible(ctx, org.AsUser(), user) {
   505  			return true
   506  		}
   507  	}
   508  	return false
   509  }
   510  
   511  // GetOrgsCanCreateRepoByUserID returns a list of organizations where given user ID
   512  // are allowed to create repos.
   513  func GetOrgsCanCreateRepoByUserID(ctx context.Context, userID int64) ([]*Organization, error) {
   514  	orgs := make([]*Organization, 0, 10)
   515  
   516  	return orgs, db.GetEngine(ctx).Where(builder.In("id", builder.Select("`user`.id").From("`user`").
   517  		Join("INNER", "`team_user`", "`team_user`.org_id = `user`.id").
   518  		Join("INNER", "`team`", "`team`.id = `team_user`.team_id").
   519  		Where(builder.Eq{"`team_user`.uid": userID}).
   520  		And(builder.Eq{"`team`.authorize": perm.AccessModeOwner}.Or(builder.Eq{"`team`.can_create_org_repo": true})))).
   521  		Asc("`user`.name").
   522  		Find(&orgs)
   523  }
   524  
   525  // GetOrgUsersByOrgID returns all organization-user relations by organization ID.
   526  func GetOrgUsersByOrgID(ctx context.Context, opts *FindOrgMembersOpts) ([]*OrgUser, error) {
   527  	sess := db.GetEngine(ctx).Where("org_id=?", opts.OrgID)
   528  	if opts.PublicOnly {
   529  		sess.And("is_public = ?", true)
   530  	}
   531  	if opts.ListOptions.PageSize > 0 {
   532  		sess = db.SetSessionPagination(sess, opts)
   533  
   534  		ous := make([]*OrgUser, 0, opts.PageSize)
   535  		return ous, sess.Find(&ous)
   536  	}
   537  
   538  	var ous []*OrgUser
   539  	return ous, sess.Find(&ous)
   540  }
   541  
   542  // ChangeOrgUserStatus changes public or private membership status.
   543  func ChangeOrgUserStatus(ctx context.Context, orgID, uid int64, public bool) error {
   544  	ou := new(OrgUser)
   545  	has, err := db.GetEngine(ctx).
   546  		Where("uid=?", uid).
   547  		And("org_id=?", orgID).
   548  		Get(ou)
   549  	if err != nil {
   550  		return err
   551  	} else if !has {
   552  		return nil
   553  	}
   554  
   555  	ou.IsPublic = public
   556  	_, err = db.GetEngine(ctx).ID(ou.ID).Cols("is_public").Update(ou)
   557  	return err
   558  }
   559  
   560  // AddOrgUser adds new user to given organization.
   561  func AddOrgUser(ctx context.Context, orgID, uid int64) error {
   562  	isAlreadyMember, err := IsOrganizationMember(ctx, orgID, uid)
   563  	if err != nil || isAlreadyMember {
   564  		return err
   565  	}
   566  
   567  	ctx, committer, err := db.TxContext(ctx)
   568  	if err != nil {
   569  		return err
   570  	}
   571  	defer committer.Close()
   572  
   573  	// check in transaction
   574  	isAlreadyMember, err = IsOrganizationMember(ctx, orgID, uid)
   575  	if err != nil || isAlreadyMember {
   576  		return err
   577  	}
   578  
   579  	ou := &OrgUser{
   580  		UID:      uid,
   581  		OrgID:    orgID,
   582  		IsPublic: setting.Service.DefaultOrgMemberVisible,
   583  	}
   584  
   585  	if err := db.Insert(ctx, ou); err != nil {
   586  		return err
   587  	} else if _, err = db.Exec(ctx, "UPDATE `user` SET num_members = num_members + 1 WHERE id = ?", orgID); err != nil {
   588  		return err
   589  	}
   590  
   591  	return committer.Commit()
   592  }
   593  
   594  // GetOrgByID returns the user object by given ID if exists.
   595  func GetOrgByID(ctx context.Context, id int64) (*Organization, error) {
   596  	u := new(Organization)
   597  	has, err := db.GetEngine(ctx).ID(id).Get(u)
   598  	if err != nil {
   599  		return nil, err
   600  	} else if !has {
   601  		return nil, user_model.ErrUserNotExist{
   602  			UID: id,
   603  		}
   604  	}
   605  	return u, nil
   606  }
   607  
   608  // RemoveOrgRepo removes all team-repository relations of organization.
   609  func RemoveOrgRepo(ctx context.Context, orgID, repoID int64) error {
   610  	teamRepos := make([]*TeamRepo, 0, 10)
   611  	e := db.GetEngine(ctx)
   612  	if err := e.Find(&teamRepos, &TeamRepo{OrgID: orgID, RepoID: repoID}); err != nil {
   613  		return err
   614  	}
   615  
   616  	if len(teamRepos) == 0 {
   617  		return nil
   618  	}
   619  
   620  	if _, err := e.Delete(&TeamRepo{
   621  		OrgID:  orgID,
   622  		RepoID: repoID,
   623  	}); err != nil {
   624  		return err
   625  	}
   626  
   627  	teamIDs := make([]int64, len(teamRepos))
   628  	for i, teamRepo := range teamRepos {
   629  		teamIDs[i] = teamRepo.TeamID
   630  	}
   631  
   632  	_, err := e.Decr("num_repos").In("id", teamIDs).Update(new(Team))
   633  	return err
   634  }
   635  
   636  func (org *Organization) getUserTeams(ctx context.Context, userID int64, cols ...string) ([]*Team, error) {
   637  	teams := make([]*Team, 0, org.NumTeams)
   638  	return teams, db.GetEngine(ctx).
   639  		Where("`team_user`.org_id = ?", org.ID).
   640  		Join("INNER", "team_user", "`team_user`.team_id = team.id").
   641  		Join("INNER", "`user`", "`user`.id=team_user.uid").
   642  		And("`team_user`.uid = ?", userID).
   643  		Asc("`user`.name").
   644  		Cols(cols...).
   645  		Find(&teams)
   646  }
   647  
   648  func (org *Organization) getUserTeamIDs(ctx context.Context, userID int64) ([]int64, error) {
   649  	teamIDs := make([]int64, 0, org.NumTeams)
   650  	return teamIDs, db.GetEngine(ctx).
   651  		Table("team").
   652  		Cols("team.id").
   653  		Where("`team_user`.org_id = ?", org.ID).
   654  		Join("INNER", "team_user", "`team_user`.team_id = team.id").
   655  		And("`team_user`.uid = ?", userID).
   656  		Find(&teamIDs)
   657  }
   658  
   659  // TeamsWithAccessToRepo returns all teams that have given access level to the repository.
   660  func (org *Organization) TeamsWithAccessToRepo(ctx context.Context, repoID int64, mode perm.AccessMode) ([]*Team, error) {
   661  	return GetTeamsWithAccessToRepo(ctx, org.ID, repoID, mode)
   662  }
   663  
   664  // GetUserTeamIDs returns of all team IDs of the organization that user is member of.
   665  func (org *Organization) GetUserTeamIDs(ctx context.Context, userID int64) ([]int64, error) {
   666  	return org.getUserTeamIDs(ctx, userID)
   667  }
   668  
   669  // GetUserTeams returns all teams that belong to user,
   670  // and that the user has joined.
   671  func (org *Organization) GetUserTeams(ctx context.Context, userID int64) ([]*Team, error) {
   672  	return org.getUserTeams(ctx, userID)
   673  }
   674  
   675  // AccessibleReposEnvironment operations involving the repositories that are
   676  // accessible to a particular user
   677  type AccessibleReposEnvironment interface {
   678  	CountRepos() (int64, error)
   679  	RepoIDs(page, pageSize int) ([]int64, error)
   680  	Repos(page, pageSize int) (repo_model.RepositoryList, error)
   681  	MirrorRepos() (repo_model.RepositoryList, error)
   682  	AddKeyword(keyword string)
   683  	SetSort(db.SearchOrderBy)
   684  }
   685  
   686  type accessibleReposEnv struct {
   687  	org     *Organization
   688  	user    *user_model.User
   689  	team    *Team
   690  	teamIDs []int64
   691  	ctx     context.Context
   692  	keyword string
   693  	orderBy db.SearchOrderBy
   694  }
   695  
   696  // AccessibleReposEnv builds an AccessibleReposEnvironment for the repositories in `org`
   697  // that are accessible to the specified user.
   698  func AccessibleReposEnv(ctx context.Context, org *Organization, userID int64) (AccessibleReposEnvironment, error) {
   699  	var user *user_model.User
   700  
   701  	if userID > 0 {
   702  		u, err := user_model.GetUserByID(ctx, userID)
   703  		if err != nil {
   704  			return nil, err
   705  		}
   706  		user = u
   707  	}
   708  
   709  	teamIDs, err := org.getUserTeamIDs(ctx, userID)
   710  	if err != nil {
   711  		return nil, err
   712  	}
   713  	return &accessibleReposEnv{
   714  		org:     org,
   715  		user:    user,
   716  		teamIDs: teamIDs,
   717  		ctx:     ctx,
   718  		orderBy: db.SearchOrderByRecentUpdated,
   719  	}, nil
   720  }
   721  
   722  // AccessibleTeamReposEnv an AccessibleReposEnvironment for the repositories in `org`
   723  // that are accessible to the specified team.
   724  func (org *Organization) AccessibleTeamReposEnv(ctx context.Context, team *Team) AccessibleReposEnvironment {
   725  	return &accessibleReposEnv{
   726  		org:     org,
   727  		team:    team,
   728  		ctx:     ctx,
   729  		orderBy: db.SearchOrderByRecentUpdated,
   730  	}
   731  }
   732  
   733  func (env *accessibleReposEnv) cond() builder.Cond {
   734  	cond := builder.NewCond()
   735  	if env.team != nil {
   736  		cond = cond.And(builder.Eq{"team_repo.team_id": env.team.ID})
   737  	} else {
   738  		if env.user == nil || !env.user.IsRestricted {
   739  			cond = cond.Or(builder.Eq{
   740  				"`repository`.owner_id":   env.org.ID,
   741  				"`repository`.is_private": false,
   742  			})
   743  		}
   744  		if len(env.teamIDs) > 0 {
   745  			cond = cond.Or(builder.In("team_repo.team_id", env.teamIDs))
   746  		}
   747  	}
   748  	if env.keyword != "" {
   749  		cond = cond.And(builder.Like{"`repository`.lower_name", strings.ToLower(env.keyword)})
   750  	}
   751  	return cond
   752  }
   753  
   754  func (env *accessibleReposEnv) CountRepos() (int64, error) {
   755  	repoCount, err := db.GetEngine(env.ctx).
   756  		Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id").
   757  		Where(env.cond()).
   758  		Distinct("`repository`.id").
   759  		Count(&repo_model.Repository{})
   760  	if err != nil {
   761  		return 0, fmt.Errorf("count user repositories in organization: %w", err)
   762  	}
   763  	return repoCount, nil
   764  }
   765  
   766  func (env *accessibleReposEnv) RepoIDs(page, pageSize int) ([]int64, error) {
   767  	if page <= 0 {
   768  		page = 1
   769  	}
   770  
   771  	repoIDs := make([]int64, 0, pageSize)
   772  	return repoIDs, db.GetEngine(env.ctx).
   773  		Table("repository").
   774  		Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id").
   775  		Where(env.cond()).
   776  		GroupBy("`repository`.id,`repository`."+strings.Fields(string(env.orderBy))[0]).
   777  		OrderBy(string(env.orderBy)).
   778  		Limit(pageSize, (page-1)*pageSize).
   779  		Cols("`repository`.id").
   780  		Find(&repoIDs)
   781  }
   782  
   783  func (env *accessibleReposEnv) Repos(page, pageSize int) (repo_model.RepositoryList, error) {
   784  	repoIDs, err := env.RepoIDs(page, pageSize)
   785  	if err != nil {
   786  		return nil, fmt.Errorf("GetUserRepositoryIDs: %w", err)
   787  	}
   788  
   789  	repos := make([]*repo_model.Repository, 0, len(repoIDs))
   790  	if len(repoIDs) == 0 {
   791  		return repos, nil
   792  	}
   793  
   794  	return repos, db.GetEngine(env.ctx).
   795  		In("`repository`.id", repoIDs).
   796  		OrderBy(string(env.orderBy)).
   797  		Find(&repos)
   798  }
   799  
   800  func (env *accessibleReposEnv) MirrorRepoIDs() ([]int64, error) {
   801  	repoIDs := make([]int64, 0, 10)
   802  	return repoIDs, db.GetEngine(env.ctx).
   803  		Table("repository").
   804  		Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id AND `repository`.is_mirror=?", true).
   805  		Where(env.cond()).
   806  		GroupBy("`repository`.id, `repository`.updated_unix").
   807  		OrderBy(string(env.orderBy)).
   808  		Cols("`repository`.id").
   809  		Find(&repoIDs)
   810  }
   811  
   812  func (env *accessibleReposEnv) MirrorRepos() (repo_model.RepositoryList, error) {
   813  	repoIDs, err := env.MirrorRepoIDs()
   814  	if err != nil {
   815  		return nil, fmt.Errorf("MirrorRepoIDs: %w", err)
   816  	}
   817  
   818  	repos := make([]*repo_model.Repository, 0, len(repoIDs))
   819  	if len(repoIDs) == 0 {
   820  		return repos, nil
   821  	}
   822  
   823  	return repos, db.GetEngine(env.ctx).
   824  		In("`repository`.id", repoIDs).
   825  		Find(&repos)
   826  }
   827  
   828  func (env *accessibleReposEnv) AddKeyword(keyword string) {
   829  	env.keyword = keyword
   830  }
   831  
   832  func (env *accessibleReposEnv) SetSort(orderBy db.SearchOrderBy) {
   833  	env.orderBy = orderBy
   834  }