code.gitea.io/gitea@v1.21.7/models/repo/collaboration.go (about)

     1  // Copyright 2022 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package repo
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  
    10  	"code.gitea.io/gitea/models/db"
    11  	"code.gitea.io/gitea/models/perm"
    12  	"code.gitea.io/gitea/models/unit"
    13  	user_model "code.gitea.io/gitea/models/user"
    14  	"code.gitea.io/gitea/modules/log"
    15  	"code.gitea.io/gitea/modules/timeutil"
    16  )
    17  
    18  // Collaboration represent the relation between an individual and a repository.
    19  type Collaboration struct {
    20  	ID          int64              `xorm:"pk autoincr"`
    21  	RepoID      int64              `xorm:"UNIQUE(s) INDEX NOT NULL"`
    22  	UserID      int64              `xorm:"UNIQUE(s) INDEX NOT NULL"`
    23  	Mode        perm.AccessMode    `xorm:"DEFAULT 2 NOT NULL"`
    24  	CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
    25  	UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
    26  }
    27  
    28  func init() {
    29  	db.RegisterModel(new(Collaboration))
    30  }
    31  
    32  // Collaborator represents a user with collaboration details.
    33  type Collaborator struct {
    34  	*user_model.User
    35  	Collaboration *Collaboration
    36  }
    37  
    38  // GetCollaborators returns the collaborators for a repository
    39  func GetCollaborators(ctx context.Context, repoID int64, listOptions db.ListOptions) ([]*Collaborator, error) {
    40  	collaborations, err := getCollaborations(ctx, repoID, listOptions)
    41  	if err != nil {
    42  		return nil, fmt.Errorf("getCollaborations: %w", err)
    43  	}
    44  
    45  	collaborators := make([]*Collaborator, 0, len(collaborations))
    46  	for _, c := range collaborations {
    47  		user, err := user_model.GetUserByID(ctx, c.UserID)
    48  		if err != nil {
    49  			if user_model.IsErrUserNotExist(err) {
    50  				log.Warn("Inconsistent DB: User: %d is listed as collaborator of %-v but does not exist", c.UserID, repoID)
    51  				user = user_model.NewGhostUser()
    52  			} else {
    53  				return nil, err
    54  			}
    55  		}
    56  		collaborators = append(collaborators, &Collaborator{
    57  			User:          user,
    58  			Collaboration: c,
    59  		})
    60  	}
    61  	return collaborators, nil
    62  }
    63  
    64  // CountCollaborators returns total number of collaborators for a repository
    65  func CountCollaborators(ctx context.Context, repoID int64) (int64, error) {
    66  	return db.GetEngine(ctx).Where("repo_id = ? ", repoID).Count(&Collaboration{})
    67  }
    68  
    69  // GetCollaboration get collaboration for a repository id with a user id
    70  func GetCollaboration(ctx context.Context, repoID, uid int64) (*Collaboration, error) {
    71  	collaboration := &Collaboration{
    72  		RepoID: repoID,
    73  		UserID: uid,
    74  	}
    75  	has, err := db.GetEngine(ctx).Get(collaboration)
    76  	if !has {
    77  		collaboration = nil
    78  	}
    79  	return collaboration, err
    80  }
    81  
    82  // IsCollaborator check if a user is a collaborator of a repository
    83  func IsCollaborator(ctx context.Context, repoID, userID int64) (bool, error) {
    84  	return db.GetEngine(ctx).Get(&Collaboration{RepoID: repoID, UserID: userID})
    85  }
    86  
    87  func getCollaborations(ctx context.Context, repoID int64, listOptions db.ListOptions) ([]*Collaboration, error) {
    88  	if listOptions.Page == 0 {
    89  		collaborations := make([]*Collaboration, 0, 8)
    90  		return collaborations, db.GetEngine(ctx).Find(&collaborations, &Collaboration{RepoID: repoID})
    91  	}
    92  
    93  	e := db.GetEngine(ctx)
    94  
    95  	e = db.SetEnginePagination(e, &listOptions)
    96  
    97  	collaborations := make([]*Collaboration, 0, listOptions.PageSize)
    98  	return collaborations, e.Find(&collaborations, &Collaboration{RepoID: repoID})
    99  }
   100  
   101  // ChangeCollaborationAccessMode sets new access mode for the collaboration.
   102  func ChangeCollaborationAccessMode(ctx context.Context, repo *Repository, uid int64, mode perm.AccessMode) error {
   103  	// Discard invalid input
   104  	if mode <= perm.AccessModeNone || mode > perm.AccessModeOwner {
   105  		return nil
   106  	}
   107  
   108  	return db.WithTx(ctx, func(ctx context.Context) error {
   109  		e := db.GetEngine(ctx)
   110  
   111  		collaboration := &Collaboration{
   112  			RepoID: repo.ID,
   113  			UserID: uid,
   114  		}
   115  		has, err := e.Get(collaboration)
   116  		if err != nil {
   117  			return fmt.Errorf("get collaboration: %w", err)
   118  		} else if !has {
   119  			return nil
   120  		}
   121  
   122  		if collaboration.Mode == mode {
   123  			return nil
   124  		}
   125  		collaboration.Mode = mode
   126  
   127  		if _, err = e.
   128  			ID(collaboration.ID).
   129  			Cols("mode").
   130  			Update(collaboration); err != nil {
   131  			return fmt.Errorf("update collaboration: %w", err)
   132  		} else if _, err = e.Exec("UPDATE access SET mode = ? WHERE user_id = ? AND repo_id = ?", mode, uid, repo.ID); err != nil {
   133  			return fmt.Errorf("update access table: %w", err)
   134  		}
   135  
   136  		return nil
   137  	})
   138  }
   139  
   140  // IsOwnerMemberCollaborator checks if a provided user is the owner, a collaborator or a member of a team in a repository
   141  func IsOwnerMemberCollaborator(ctx context.Context, repo *Repository, userID int64) (bool, error) {
   142  	if repo.OwnerID == userID {
   143  		return true, nil
   144  	}
   145  	teamMember, err := db.GetEngine(ctx).Join("INNER", "team_repo", "team_repo.team_id = team_user.team_id").
   146  		Join("INNER", "team_unit", "team_unit.team_id = team_user.team_id").
   147  		Where("team_repo.repo_id = ?", repo.ID).
   148  		And("team_unit.`type` = ?", unit.TypeCode).
   149  		And("team_user.uid = ?", userID).Table("team_user").Exist()
   150  	if err != nil {
   151  		return false, err
   152  	}
   153  	if teamMember {
   154  		return true, nil
   155  	}
   156  
   157  	return db.GetEngine(ctx).Get(&Collaboration{RepoID: repo.ID, UserID: userID})
   158  }