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