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

access.go 6.3 kB

11 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. // Copyright 2014 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 models
  5. import (
  6. "fmt"
  7. "github.com/gogits/gogs/modules/log"
  8. )
  9. type AccessMode int
  10. const (
  11. ACCESS_MODE_NONE AccessMode = iota
  12. ACCESS_MODE_READ
  13. ACCESS_MODE_WRITE
  14. ACCESS_MODE_ADMIN
  15. ACCESS_MODE_OWNER
  16. )
  17. // Access represents the highest access level of a user to the repository. The only access type
  18. // that is not in this table is the real owner of a repository. In case of an organization
  19. // repository, the members of the owners team are in this table.
  20. type Access struct {
  21. ID int64 `xorm:"pk autoincr"`
  22. UserID int64 `xorm:"UNIQUE(s)"`
  23. RepoID int64 `xorm:"UNIQUE(s)"`
  24. Mode AccessMode
  25. }
  26. func accessLevel(e Engine, u *User, repo *Repository) (AccessMode, error) {
  27. mode := ACCESS_MODE_NONE
  28. if !repo.IsPrivate {
  29. mode = ACCESS_MODE_READ
  30. }
  31. if u == nil {
  32. return mode, nil
  33. }
  34. if u.Id == repo.OwnerID {
  35. return ACCESS_MODE_OWNER, nil
  36. }
  37. a := &Access{UserID: u.Id, RepoID: repo.ID}
  38. if has, err := e.Get(a); !has || err != nil {
  39. return mode, err
  40. }
  41. return a.Mode, nil
  42. }
  43. // AccessLevel returns the Access a user has to a repository. Will return NoneAccess if the
  44. // user does not have access. User can be nil!
  45. func AccessLevel(u *User, repo *Repository) (AccessMode, error) {
  46. return accessLevel(x, u, repo)
  47. }
  48. func hasAccess(e Engine, u *User, repo *Repository, testMode AccessMode) (bool, error) {
  49. mode, err := accessLevel(e, u, repo)
  50. return testMode <= mode, err
  51. }
  52. // HasAccess returns true if someone has the request access level. User can be nil!
  53. func HasAccess(u *User, repo *Repository, testMode AccessMode) (bool, error) {
  54. return hasAccess(x, u, repo, testMode)
  55. }
  56. // GetRepositoryAccesses finds all repositories with their access mode where a user has access but does not own.
  57. func (u *User) GetRepositoryAccesses() (map[*Repository]AccessMode, error) {
  58. accesses := make([]*Access, 0, 10)
  59. if err := x.Find(&accesses, &Access{UserID: u.Id}); err != nil {
  60. return nil, err
  61. }
  62. repos := make(map[*Repository]AccessMode, len(accesses))
  63. for _, access := range accesses {
  64. repo, err := GetRepositoryByID(access.RepoID)
  65. if err != nil {
  66. if IsErrRepoNotExist(err) {
  67. log.Error(4, "GetRepositoryByID: %v", err)
  68. continue
  69. }
  70. return nil, err
  71. }
  72. if err = repo.GetOwner(); err != nil {
  73. return nil, err
  74. } else if repo.OwnerID == u.Id {
  75. continue
  76. }
  77. repos[repo] = access.Mode
  78. }
  79. return repos, nil
  80. }
  81. // GetAccessibleRepositories finds all repositories where a user has access but does not own.
  82. func (u *User) GetAccessibleRepositories() ([]*Repository, error) {
  83. accesses := make([]*Access, 0, 10)
  84. if err := x.Find(&accesses, &Access{UserID: u.Id}); err != nil {
  85. return nil, err
  86. }
  87. if len(accesses) == 0 {
  88. return []*Repository{}, nil
  89. }
  90. repoIDs := make([]int64, 0, len(accesses))
  91. for _, access := range accesses {
  92. repoIDs = append(repoIDs, access.RepoID)
  93. }
  94. repos := make([]*Repository, 0, len(repoIDs))
  95. return repos, x.Where("owner_id != ?", u.Id).In("id", repoIDs).Desc("updated").Find(&repos)
  96. }
  97. func maxAccessMode(modes ...AccessMode) AccessMode {
  98. max := ACCESS_MODE_NONE
  99. for _, mode := range modes {
  100. if mode > max {
  101. max = mode
  102. }
  103. }
  104. return max
  105. }
  106. // FIXME: do corss-comparison so reduce deletions and additions to the minimum?
  107. func (repo *Repository) refreshAccesses(e Engine, accessMap map[int64]AccessMode) (err error) {
  108. minMode := ACCESS_MODE_READ
  109. if !repo.IsPrivate {
  110. minMode = ACCESS_MODE_WRITE
  111. }
  112. newAccesses := make([]Access, 0, len(accessMap))
  113. for userID, mode := range accessMap {
  114. if mode < minMode {
  115. continue
  116. }
  117. newAccesses = append(newAccesses, Access{
  118. UserID: userID,
  119. RepoID: repo.ID,
  120. Mode: mode,
  121. })
  122. }
  123. // Delete old accesses and insert new ones for repository.
  124. if _, err = e.Delete(&Access{RepoID: repo.ID}); err != nil {
  125. return fmt.Errorf("delete old accesses: %v", err)
  126. } else if _, err = e.Insert(newAccesses); err != nil {
  127. return fmt.Errorf("insert new accesses: %v", err)
  128. }
  129. return nil
  130. }
  131. // FIXME: should be able to have read-only access.
  132. // Give all collaborators write access.
  133. func (repo *Repository) refreshCollaboratorAccesses(e Engine, accessMap map[int64]AccessMode) error {
  134. collaborators, err := repo.getCollaborators(e)
  135. if err != nil {
  136. return fmt.Errorf("getCollaborators: %v", err)
  137. }
  138. for _, c := range collaborators {
  139. accessMap[c.Id] = ACCESS_MODE_WRITE
  140. }
  141. return nil
  142. }
  143. // recalculateTeamAccesses recalculates new accesses for teams of an organization
  144. // except the team whose ID is given. It is used to assign a team ID when
  145. // remove repository from that team.
  146. func (repo *Repository) recalculateTeamAccesses(e Engine, ignTeamID int64) (err error) {
  147. accessMap := make(map[int64]AccessMode, 20)
  148. if err = repo.getOwner(e); err != nil {
  149. return err
  150. } else if !repo.Owner.IsOrganization() {
  151. return fmt.Errorf("owner is not an organization: %d", repo.OwnerID)
  152. }
  153. if err = repo.refreshCollaboratorAccesses(e, accessMap); err != nil {
  154. return fmt.Errorf("refreshCollaboratorAccesses: %v", err)
  155. }
  156. if err = repo.Owner.getTeams(e); err != nil {
  157. return err
  158. }
  159. for _, t := range repo.Owner.Teams {
  160. if t.ID == ignTeamID {
  161. continue
  162. }
  163. // Owner team gets owner access, and skip for teams that do not
  164. // have relations with repository.
  165. if t.IsOwnerTeam() {
  166. t.Authorize = ACCESS_MODE_OWNER
  167. } else if !t.hasRepository(e, repo.ID) {
  168. continue
  169. }
  170. if err = t.getMembers(e); err != nil {
  171. return fmt.Errorf("getMembers '%d': %v", t.ID, err)
  172. }
  173. for _, m := range t.Members {
  174. accessMap[m.Id] = maxAccessMode(accessMap[m.Id], t.Authorize)
  175. }
  176. }
  177. return repo.refreshAccesses(e, accessMap)
  178. }
  179. func (repo *Repository) recalculateAccesses(e Engine) error {
  180. if repo.Owner.IsOrganization() {
  181. return repo.recalculateTeamAccesses(e, 0)
  182. }
  183. accessMap := make(map[int64]AccessMode, 20)
  184. if err := repo.refreshCollaboratorAccesses(e, accessMap); err != nil {
  185. return fmt.Errorf("refreshCollaboratorAccesses: %v", err)
  186. }
  187. return repo.refreshAccesses(e, accessMap)
  188. }
  189. // RecalculateAccesses recalculates all accesses for repository.
  190. func (r *Repository) RecalculateAccesses() error {
  191. return r.recalculateAccesses(x)
  192. }