repo_collaboration.go 5.7 KB

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