repo_collaboration.go 5.9 KB

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