repo_collaboration.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. package database
  2. import (
  3. log "unknwon.dev/clog/v2"
  4. "github.com/cockroachdb/errors"
  5. api "github.com/gogs/go-gogs-client"
  6. "gorm.io/gorm"
  7. )
  8. // Collaboration represent the relation between an individual and a repository.
  9. type Collaboration struct {
  10. ID int64 `gorm:"primary_key"`
  11. UserID int64 `xorm:"UNIQUE(s) INDEX NOT NULL" gorm:"uniqueIndex:collaboration_user_repo_unique;index;not null"`
  12. RepoID int64 `xorm:"UNIQUE(s) INDEX NOT NULL" gorm:"uniqueIndex:collaboration_user_repo_unique;index;not null"`
  13. Mode AccessMode `xorm:"DEFAULT 2 NOT NULL" gorm:"not null;default:2"`
  14. }
  15. func (c *Collaboration) ModeI18nKey() string {
  16. switch c.Mode {
  17. case AccessModeRead:
  18. return "repo.settings.collaboration.read"
  19. case AccessModeWrite:
  20. return "repo.settings.collaboration.write"
  21. case AccessModeAdmin:
  22. return "repo.settings.collaboration.admin"
  23. default:
  24. return "repo.settings.collaboration.undefined"
  25. }
  26. }
  27. // IsCollaborator returns true if the user is a collaborator of the repository.
  28. func IsCollaborator(repoID, userID int64) bool {
  29. collaboration := &Collaboration{
  30. RepoID: repoID,
  31. UserID: userID,
  32. }
  33. has, err := x.Get(collaboration)
  34. if err != nil {
  35. log.Error("get collaboration [repo_id: %d, user_id: %d]: %v", repoID, userID, err)
  36. return false
  37. }
  38. return has
  39. }
  40. func (r *Repository) IsCollaborator(userID int64) bool {
  41. return IsCollaborator(r.ID, userID)
  42. }
  43. // AddCollaborator adds new collaboration to a repository with default access mode.
  44. func (r *Repository) AddCollaborator(u *User) error {
  45. collaboration := &Collaboration{
  46. RepoID: r.ID,
  47. UserID: u.ID,
  48. }
  49. has, err := x.Get(collaboration)
  50. if err != nil {
  51. return err
  52. } else if has {
  53. return nil
  54. }
  55. collaboration.Mode = AccessModeWrite
  56. sess := x.NewSession()
  57. defer sess.Close()
  58. if err = sess.Begin(); err != nil {
  59. return err
  60. }
  61. if _, err = sess.Insert(collaboration); err != nil {
  62. return err
  63. } else if err = r.recalculateAccesses(sess); err != nil {
  64. return errors.Newf("recalculateAccesses [repo_id: %v]: %v", r.ID, err)
  65. }
  66. return sess.Commit()
  67. }
  68. func (r *Repository) getCollaborations(e *gorm.DB) ([]*Collaboration, error) {
  69. collaborations := make([]*Collaboration, 0)
  70. return collaborations, e.Where("repo_id = ?", r.ID).Find(&collaborations).Error
  71. }
  72. // Collaborator represents a user with collaboration details.
  73. type Collaborator struct {
  74. *User
  75. Collaboration *Collaboration
  76. }
  77. func (c *Collaborator) APIFormat() *api.Collaborator {
  78. return &api.Collaborator{
  79. User: c.User.APIFormat(),
  80. Permissions: api.Permission{
  81. Admin: c.Collaboration.Mode >= AccessModeAdmin,
  82. Push: c.Collaboration.Mode >= AccessModeWrite,
  83. Pull: c.Collaboration.Mode >= AccessModeRead,
  84. },
  85. }
  86. }
  87. func (r *Repository) getCollaborators(e *gorm.DB) ([]*Collaborator, error) {
  88. collaborations, err := r.getCollaborations(e)
  89. if err != nil {
  90. return nil, errors.Newf("getCollaborations: %v", err)
  91. }
  92. collaborators := make([]*Collaborator, len(collaborations))
  93. for i, c := range collaborations {
  94. user, err := getUserByID(e, c.UserID)
  95. if err != nil {
  96. return nil, err
  97. }
  98. collaborators[i] = &Collaborator{
  99. User: user,
  100. Collaboration: c,
  101. }
  102. }
  103. return collaborators, nil
  104. }
  105. // GetCollaborators returns the collaborators for a repository
  106. func (r *Repository) GetCollaborators() ([]*Collaborator, error) {
  107. return r.getCollaborators(x)
  108. }
  109. // ChangeCollaborationAccessMode sets new access mode for the collaboration.
  110. func (r *Repository) ChangeCollaborationAccessMode(userID int64, mode AccessMode) error {
  111. // Discard invalid input
  112. if mode <= AccessModeNone || mode > AccessModeOwner {
  113. return nil
  114. }
  115. collaboration := &Collaboration{
  116. RepoID: r.ID,
  117. UserID: userID,
  118. }
  119. has, err := x.Get(collaboration)
  120. if err != nil {
  121. return errors.Newf("get collaboration: %v", err)
  122. } else if !has {
  123. return nil
  124. }
  125. if collaboration.Mode == mode {
  126. return nil
  127. }
  128. collaboration.Mode = mode
  129. // If it's an organizational repository, merge with team access level for highest permission
  130. if r.Owner.IsOrganization() {
  131. teams, err := GetUserTeams(r.OwnerID, userID)
  132. if err != nil {
  133. return errors.Newf("GetUserTeams: [org_id: %d, user_id: %d]: %v", r.OwnerID, userID, err)
  134. }
  135. for i := range teams {
  136. if mode < teams[i].Authorize {
  137. mode = teams[i].Authorize
  138. }
  139. }
  140. }
  141. sess := x.NewSession()
  142. defer sess.Close()
  143. if err = sess.Begin(); err != nil {
  144. return err
  145. }
  146. if _, err = sess.ID(collaboration.ID).AllCols().Update(collaboration); err != nil {
  147. return errors.Newf("update collaboration: %v", err)
  148. }
  149. access := &Access{
  150. UserID: userID,
  151. RepoID: r.ID,
  152. }
  153. has, err = sess.Get(access)
  154. if err != nil {
  155. return errors.Newf("get access record: %v", err)
  156. }
  157. if has {
  158. _, err = sess.Exec("UPDATE access SET mode = ? WHERE user_id = ? AND repo_id = ?", mode, userID, r.ID)
  159. } else {
  160. access.Mode = mode
  161. _, err = sess.Insert(access)
  162. }
  163. if err != nil {
  164. return errors.Newf("update/insert access table: %v", err)
  165. }
  166. return sess.Commit()
  167. }
  168. // DeleteCollaboration removes collaboration relation between the user and repository.
  169. func DeleteCollaboration(repo *Repository, userID int64) (err error) {
  170. if !IsCollaborator(repo.ID, userID) {
  171. return nil
  172. }
  173. collaboration := &Collaboration{
  174. RepoID: repo.ID,
  175. UserID: userID,
  176. }
  177. sess := x.NewSession()
  178. defer sess.Close()
  179. if err = sess.Begin(); err != nil {
  180. return err
  181. }
  182. if has, err := sess.Delete(collaboration); err != nil || has == 0 {
  183. return err
  184. } else if err = repo.recalculateAccesses(sess); err != nil {
  185. return err
  186. }
  187. return sess.Commit()
  188. }
  189. func (r *Repository) DeleteCollaboration(userID int64) error {
  190. return DeleteCollaboration(r, userID)
  191. }