You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

collaboration.go 5.0KB

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