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 7.0 kB

11 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  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. "code.gitea.io/gitea/modules/log"
  8. )
  9. // AccessMode specifies the users access mode
  10. type AccessMode int
  11. const (
  12. // AccessModeNone no access
  13. AccessModeNone AccessMode = iota // 0
  14. // AccessModeRead read access
  15. AccessModeRead // 1
  16. // AccessModeWrite write access
  17. AccessModeWrite // 2
  18. // AccessModeAdmin admin access
  19. AccessModeAdmin // 3
  20. // AccessModeOwner owner access
  21. AccessModeOwner // 4
  22. )
  23. func (mode AccessMode) String() string {
  24. switch mode {
  25. case AccessModeRead:
  26. return "read"
  27. case AccessModeWrite:
  28. return "write"
  29. case AccessModeAdmin:
  30. return "admin"
  31. case AccessModeOwner:
  32. return "owner"
  33. default:
  34. return "none"
  35. }
  36. }
  37. // ParseAccessMode returns corresponding access mode to given permission string.
  38. func ParseAccessMode(permission string) AccessMode {
  39. switch permission {
  40. case "write":
  41. return AccessModeWrite
  42. case "admin":
  43. return AccessModeAdmin
  44. default:
  45. return AccessModeRead
  46. }
  47. }
  48. // Access represents the highest access level of a user to the repository. The only access type
  49. // that is not in this table is the real owner of a repository. In case of an organization
  50. // repository, the members of the owners team are in this table.
  51. type Access struct {
  52. ID int64 `xorm:"pk autoincr"`
  53. UserID int64 `xorm:"UNIQUE(s)"`
  54. RepoID int64 `xorm:"UNIQUE(s)"`
  55. Mode AccessMode
  56. }
  57. func accessLevel(e Engine, user *User, repo *Repository) (AccessMode, error) {
  58. mode := AccessModeNone
  59. if !repo.IsPrivate {
  60. mode = AccessModeRead
  61. }
  62. if user == nil {
  63. return mode, nil
  64. }
  65. if user.ID == repo.OwnerID {
  66. return AccessModeOwner, nil
  67. }
  68. a := &Access{UserID: user.ID, RepoID: repo.ID}
  69. if has, err := e.Get(a); !has || err != nil {
  70. return mode, err
  71. }
  72. return a.Mode, nil
  73. }
  74. // AccessLevel returns the Access a user has to a repository. Will return NoneAccess if the
  75. // user does not have access. User can be nil!
  76. func AccessLevel(user *User, repo *Repository) (AccessMode, error) {
  77. return accessLevel(x, user, repo)
  78. }
  79. func hasAccess(e Engine, user *User, repo *Repository, testMode AccessMode) (bool, error) {
  80. mode, err := accessLevel(e, user, repo)
  81. return testMode <= mode, err
  82. }
  83. // HasAccess returns true if someone has the request access level. User can be nil!
  84. func HasAccess(user *User, repo *Repository, testMode AccessMode) (bool, error) {
  85. return hasAccess(x, user, repo, testMode)
  86. }
  87. // GetRepositoryAccesses finds all repositories with their access mode where a user has access but does not own.
  88. func (user *User) GetRepositoryAccesses() (map[*Repository]AccessMode, error) {
  89. accesses := make([]*Access, 0, 10)
  90. if err := x.Find(&accesses, &Access{UserID: user.ID}); err != nil {
  91. return nil, err
  92. }
  93. repos := make(map[*Repository]AccessMode, len(accesses))
  94. for _, access := range accesses {
  95. repo, err := GetRepositoryByID(access.RepoID)
  96. if err != nil {
  97. if IsErrRepoNotExist(err) {
  98. log.Error(4, "GetRepositoryByID: %v", err)
  99. continue
  100. }
  101. return nil, err
  102. }
  103. if err = repo.GetOwner(); err != nil {
  104. return nil, err
  105. } else if repo.OwnerID == user.ID {
  106. continue
  107. }
  108. repos[repo] = access.Mode
  109. }
  110. return repos, nil
  111. }
  112. // GetAccessibleRepositories finds repositories which the user has access but does not own.
  113. // If limit is smaller than 1 means returns all found results.
  114. func (user *User) GetAccessibleRepositories(limit int) (repos []*Repository, _ error) {
  115. sess := x.
  116. Where("owner_id !=? ", user.ID).
  117. Desc("updated_unix")
  118. if limit > 0 {
  119. sess.Limit(limit)
  120. repos = make([]*Repository, 0, limit)
  121. } else {
  122. repos = make([]*Repository, 0, 10)
  123. }
  124. return repos, sess.
  125. Join("INNER", "access", "access.user_id = ? AND access.repo_id = repository.id", user.ID).
  126. Find(&repos)
  127. }
  128. func maxAccessMode(modes ...AccessMode) AccessMode {
  129. max := AccessModeNone
  130. for _, mode := range modes {
  131. if mode > max {
  132. max = mode
  133. }
  134. }
  135. return max
  136. }
  137. // FIXME: do corss-comparison so reduce deletions and additions to the minimum?
  138. func (repo *Repository) refreshAccesses(e Engine, accessMap map[int64]AccessMode) (err error) {
  139. minMode := AccessModeRead
  140. if !repo.IsPrivate {
  141. minMode = AccessModeWrite
  142. }
  143. newAccesses := make([]Access, 0, len(accessMap))
  144. for userID, mode := range accessMap {
  145. if mode < minMode {
  146. continue
  147. }
  148. newAccesses = append(newAccesses, Access{
  149. UserID: userID,
  150. RepoID: repo.ID,
  151. Mode: mode,
  152. })
  153. }
  154. // Delete old accesses and insert new ones for repository.
  155. if _, err = e.Delete(&Access{RepoID: repo.ID}); err != nil {
  156. return fmt.Errorf("delete old accesses: %v", err)
  157. } else if _, err = e.Insert(newAccesses); err != nil {
  158. return fmt.Errorf("insert new accesses: %v", err)
  159. }
  160. return nil
  161. }
  162. // refreshCollaboratorAccesses retrieves repository collaborations with their access modes.
  163. func (repo *Repository) refreshCollaboratorAccesses(e Engine, accessMap map[int64]AccessMode) error {
  164. collaborations, err := repo.getCollaborations(e)
  165. if err != nil {
  166. return fmt.Errorf("getCollaborations: %v", err)
  167. }
  168. for _, c := range collaborations {
  169. accessMap[c.UserID] = c.Mode
  170. }
  171. return nil
  172. }
  173. // recalculateTeamAccesses recalculates new accesses for teams of an organization
  174. // except the team whose ID is given. It is used to assign a team ID when
  175. // remove repository from that team.
  176. func (repo *Repository) recalculateTeamAccesses(e Engine, ignTeamID int64) (err error) {
  177. accessMap := make(map[int64]AccessMode, 20)
  178. if err = repo.getOwner(e); err != nil {
  179. return err
  180. } else if !repo.Owner.IsOrganization() {
  181. return fmt.Errorf("owner is not an organization: %d", repo.OwnerID)
  182. }
  183. if err = repo.refreshCollaboratorAccesses(e, accessMap); err != nil {
  184. return fmt.Errorf("refreshCollaboratorAccesses: %v", err)
  185. }
  186. if err = repo.Owner.getTeams(e); err != nil {
  187. return err
  188. }
  189. for _, t := range repo.Owner.Teams {
  190. if t.ID == ignTeamID {
  191. continue
  192. }
  193. // Owner team gets owner access, and skip for teams that do not
  194. // have relations with repository.
  195. if t.IsOwnerTeam() {
  196. t.Authorize = AccessModeOwner
  197. } else if !t.hasRepository(e, repo.ID) {
  198. continue
  199. }
  200. if err = t.getMembers(e); err != nil {
  201. return fmt.Errorf("getMembers '%d': %v", t.ID, err)
  202. }
  203. for _, m := range t.Members {
  204. accessMap[m.ID] = maxAccessMode(accessMap[m.ID], t.Authorize)
  205. }
  206. }
  207. return repo.refreshAccesses(e, accessMap)
  208. }
  209. func (repo *Repository) recalculateAccesses(e Engine) error {
  210. if repo.Owner.IsOrganization() {
  211. return repo.recalculateTeamAccesses(e, 0)
  212. }
  213. accessMap := make(map[int64]AccessMode, 20)
  214. if err := repo.refreshCollaboratorAccesses(e, accessMap); err != nil {
  215. return fmt.Errorf("refreshCollaboratorAccesses: %v", err)
  216. }
  217. return repo.refreshAccesses(e, accessMap)
  218. }
  219. // RecalculateAccesses recalculates all accesses for repository.
  220. func (repo *Repository) RecalculateAccesses() error {
  221. return repo.recalculateAccesses(x)
  222. }