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

repo_permission.go 13KB

Rewrite logger system (#24726) ## ⚠️ Breaking The `log.<mode>.<logger>` style config has been dropped. If you used it, please check the new config manual & app.example.ini to make your instance output logs as expected. Although many legacy options still work, it's encouraged to upgrade to the new options. The SMTP logger is deleted because SMTP is not suitable to collect logs. If you have manually configured Gitea log options, please confirm the logger system works as expected after upgrading. ## Description Close #12082 and maybe more log-related issues, resolve some related FIXMEs in old code (which seems unfixable before) Just like rewriting queue #24505 : make code maintainable, clear legacy bugs, and add the ability to support more writers (eg: JSON, structured log) There is a new document (with examples): `logging-config.en-us.md` This PR is safer than the queue rewriting, because it's just for logging, it won't break other logic. ## The old problems The logging system is quite old and difficult to maintain: * Unclear concepts: Logger, NamedLogger, MultiChannelledLogger, SubLogger, EventLogger, WriterLogger etc * Some code is diffuclt to konw whether it is right: `log.DelNamedLogger("console")` vs `log.DelNamedLogger(log.DEFAULT)` vs `log.DelLogger("console")` * The old system heavily depends on ini config system, it's difficult to create new logger for different purpose, and it's very fragile. * The "color" trick is difficult to use and read, many colors are unnecessary, and in the future structured log could help * It's difficult to add other log formats, eg: JSON format * The log outputer doesn't have full control of its goroutine, it's difficult to make outputer have advanced behaviors * The logs could be lost in some cases: eg: no Fatal error when using CLI. * Config options are passed by JSON, which is quite fragile. * INI package makes the KEY in `[log]` section visible in `[log.sub1]` and `[log.sub1.subA]`, this behavior is quite fragile and would cause more unclear problems, and there is no strong requirement to support `log.<mode>.<logger>` syntax. ## The new design See `logger.go` for documents. ## Screenshot <details> ![image](https://github.com/go-gitea/gitea/assets/2114189/4462d713-ba39-41f5-bb08-de912e67e1ff) ![image](https://github.com/go-gitea/gitea/assets/2114189/b188035e-f691-428b-8b2d-ff7b2199b2f9) ![image](https://github.com/go-gitea/gitea/assets/2114189/132e9745-1c3b-4e00-9e0d-15eaea495dee) </details> ## TODO * [x] add some new tests * [x] fix some tests * [x] test some sub-commands (manually ....) --------- Co-authored-by: Jason Song <i@wolfogre.com> Co-authored-by: delvh <dev.lh@web.de> Co-authored-by: Giteabot <teabot@gitea.io>
1 year ago
Rewrite logger system (#24726) ## ⚠️ Breaking The `log.<mode>.<logger>` style config has been dropped. If you used it, please check the new config manual & app.example.ini to make your instance output logs as expected. Although many legacy options still work, it's encouraged to upgrade to the new options. The SMTP logger is deleted because SMTP is not suitable to collect logs. If you have manually configured Gitea log options, please confirm the logger system works as expected after upgrading. ## Description Close #12082 and maybe more log-related issues, resolve some related FIXMEs in old code (which seems unfixable before) Just like rewriting queue #24505 : make code maintainable, clear legacy bugs, and add the ability to support more writers (eg: JSON, structured log) There is a new document (with examples): `logging-config.en-us.md` This PR is safer than the queue rewriting, because it's just for logging, it won't break other logic. ## The old problems The logging system is quite old and difficult to maintain: * Unclear concepts: Logger, NamedLogger, MultiChannelledLogger, SubLogger, EventLogger, WriterLogger etc * Some code is diffuclt to konw whether it is right: `log.DelNamedLogger("console")` vs `log.DelNamedLogger(log.DEFAULT)` vs `log.DelLogger("console")` * The old system heavily depends on ini config system, it's difficult to create new logger for different purpose, and it's very fragile. * The "color" trick is difficult to use and read, many colors are unnecessary, and in the future structured log could help * It's difficult to add other log formats, eg: JSON format * The log outputer doesn't have full control of its goroutine, it's difficult to make outputer have advanced behaviors * The logs could be lost in some cases: eg: no Fatal error when using CLI. * Config options are passed by JSON, which is quite fragile. * INI package makes the KEY in `[log]` section visible in `[log.sub1]` and `[log.sub1.subA]`, this behavior is quite fragile and would cause more unclear problems, and there is no strong requirement to support `log.<mode>.<logger>` syntax. ## The new design See `logger.go` for documents. ## Screenshot <details> ![image](https://github.com/go-gitea/gitea/assets/2114189/4462d713-ba39-41f5-bb08-de912e67e1ff) ![image](https://github.com/go-gitea/gitea/assets/2114189/b188035e-f691-428b-8b2d-ff7b2199b2f9) ![image](https://github.com/go-gitea/gitea/assets/2114189/132e9745-1c3b-4e00-9e0d-15eaea495dee) </details> ## TODO * [x] add some new tests * [x] fix some tests * [x] test some sub-commands (manually ....) --------- Co-authored-by: Jason Song <i@wolfogre.com> Co-authored-by: delvh <dev.lh@web.de> Co-authored-by: Giteabot <teabot@gitea.io>
1 year ago
Rewrite logger system (#24726) ## ⚠️ Breaking The `log.<mode>.<logger>` style config has been dropped. If you used it, please check the new config manual & app.example.ini to make your instance output logs as expected. Although many legacy options still work, it's encouraged to upgrade to the new options. The SMTP logger is deleted because SMTP is not suitable to collect logs. If you have manually configured Gitea log options, please confirm the logger system works as expected after upgrading. ## Description Close #12082 and maybe more log-related issues, resolve some related FIXMEs in old code (which seems unfixable before) Just like rewriting queue #24505 : make code maintainable, clear legacy bugs, and add the ability to support more writers (eg: JSON, structured log) There is a new document (with examples): `logging-config.en-us.md` This PR is safer than the queue rewriting, because it's just for logging, it won't break other logic. ## The old problems The logging system is quite old and difficult to maintain: * Unclear concepts: Logger, NamedLogger, MultiChannelledLogger, SubLogger, EventLogger, WriterLogger etc * Some code is diffuclt to konw whether it is right: `log.DelNamedLogger("console")` vs `log.DelNamedLogger(log.DEFAULT)` vs `log.DelLogger("console")` * The old system heavily depends on ini config system, it's difficult to create new logger for different purpose, and it's very fragile. * The "color" trick is difficult to use and read, many colors are unnecessary, and in the future structured log could help * It's difficult to add other log formats, eg: JSON format * The log outputer doesn't have full control of its goroutine, it's difficult to make outputer have advanced behaviors * The logs could be lost in some cases: eg: no Fatal error when using CLI. * Config options are passed by JSON, which is quite fragile. * INI package makes the KEY in `[log]` section visible in `[log.sub1]` and `[log.sub1.subA]`, this behavior is quite fragile and would cause more unclear problems, and there is no strong requirement to support `log.<mode>.<logger>` syntax. ## The new design See `logger.go` for documents. ## Screenshot <details> ![image](https://github.com/go-gitea/gitea/assets/2114189/4462d713-ba39-41f5-bb08-de912e67e1ff) ![image](https://github.com/go-gitea/gitea/assets/2114189/b188035e-f691-428b-8b2d-ff7b2199b2f9) ![image](https://github.com/go-gitea/gitea/assets/2114189/132e9745-1c3b-4e00-9e0d-15eaea495dee) </details> ## TODO * [x] add some new tests * [x] fix some tests * [x] test some sub-commands (manually ....) --------- Co-authored-by: Jason Song <i@wolfogre.com> Co-authored-by: delvh <dev.lh@web.de> Co-authored-by: Giteabot <teabot@gitea.io>
1 year ago
Team permission allow different unit has different permission (#17811) * Team permission allow different unit has different permission * Finish the interface and the logic * Fix lint * Fix translation * align center for table cell content * Fix fixture * merge * Fix test * Add deprecated * Improve code * Add tooltip * Fix swagger * Fix newline * Fix tests * Fix tests * Fix test * Fix test * Max permission of external wiki and issues should be read * Move team units with limited max level below units table * Update label and column names * Some improvements * Fix lint * Some improvements * Fix template variables * Add permission docs * improve doc * Fix fixture * Fix bug * Fix some bug * fix * gofumpt * Integration test for migration (#18124) integrations: basic test for Gitea {dump,restore}-repo This is a first step for integration testing of DumpRepository and RestoreRepository. It: runs a Gitea server, dumps a repo via DumpRepository to the filesystem, restores the repo via RestoreRepository from the filesystem, dumps the restored repository to the filesystem, compares the first and second dump and expects them to be identical The verification is trivial and the goal is to add more tests for each topic of the dump. Signed-off-by: Loïc Dachary <loic@dachary.org> * Team permission allow different unit has different permission * Finish the interface and the logic * Fix lint * Fix translation * align center for table cell content * Fix fixture * merge * Fix test * Add deprecated * Improve code * Add tooltip * Fix swagger * Fix newline * Fix tests * Fix tests * Fix test * Fix test * Max permission of external wiki and issues should be read * Move team units with limited max level below units table * Update label and column names * Some improvements * Fix lint * Some improvements * Fix template variables * Add permission docs * improve doc * Fix fixture * Fix bug * Fix some bug * Fix bug Co-authored-by: Lauris BH <lauris@nix.lv> Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: Aravinth Manivannan <realaravinth@batsense.net>
2 years ago
Team permission allow different unit has different permission (#17811) * Team permission allow different unit has different permission * Finish the interface and the logic * Fix lint * Fix translation * align center for table cell content * Fix fixture * merge * Fix test * Add deprecated * Improve code * Add tooltip * Fix swagger * Fix newline * Fix tests * Fix tests * Fix test * Fix test * Max permission of external wiki and issues should be read * Move team units with limited max level below units table * Update label and column names * Some improvements * Fix lint * Some improvements * Fix template variables * Add permission docs * improve doc * Fix fixture * Fix bug * Fix some bug * fix * gofumpt * Integration test for migration (#18124) integrations: basic test for Gitea {dump,restore}-repo This is a first step for integration testing of DumpRepository and RestoreRepository. It: runs a Gitea server, dumps a repo via DumpRepository to the filesystem, restores the repo via RestoreRepository from the filesystem, dumps the restored repository to the filesystem, compares the first and second dump and expects them to be identical The verification is trivial and the goal is to add more tests for each topic of the dump. Signed-off-by: Loïc Dachary <loic@dachary.org> * Team permission allow different unit has different permission * Finish the interface and the logic * Fix lint * Fix translation * align center for table cell content * Fix fixture * merge * Fix test * Add deprecated * Improve code * Add tooltip * Fix swagger * Fix newline * Fix tests * Fix tests * Fix test * Fix test * Max permission of external wiki and issues should be read * Move team units with limited max level below units table * Update label and column names * Some improvements * Fix lint * Some improvements * Fix template variables * Add permission docs * improve doc * Fix fixture * Fix bug * Fix some bug * Fix bug Co-authored-by: Lauris BH <lauris@nix.lv> Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: Aravinth Manivannan <realaravinth@batsense.net>
2 years ago
Team permission allow different unit has different permission (#17811) * Team permission allow different unit has different permission * Finish the interface and the logic * Fix lint * Fix translation * align center for table cell content * Fix fixture * merge * Fix test * Add deprecated * Improve code * Add tooltip * Fix swagger * Fix newline * Fix tests * Fix tests * Fix test * Fix test * Max permission of external wiki and issues should be read * Move team units with limited max level below units table * Update label and column names * Some improvements * Fix lint * Some improvements * Fix template variables * Add permission docs * improve doc * Fix fixture * Fix bug * Fix some bug * fix * gofumpt * Integration test for migration (#18124) integrations: basic test for Gitea {dump,restore}-repo This is a first step for integration testing of DumpRepository and RestoreRepository. It: runs a Gitea server, dumps a repo via DumpRepository to the filesystem, restores the repo via RestoreRepository from the filesystem, dumps the restored repository to the filesystem, compares the first and second dump and expects them to be identical The verification is trivial and the goal is to add more tests for each topic of the dump. Signed-off-by: Loïc Dachary <loic@dachary.org> * Team permission allow different unit has different permission * Finish the interface and the logic * Fix lint * Fix translation * align center for table cell content * Fix fixture * merge * Fix test * Add deprecated * Improve code * Add tooltip * Fix swagger * Fix newline * Fix tests * Fix tests * Fix test * Fix test * Max permission of external wiki and issues should be read * Move team units with limited max level below units table * Update label and column names * Some improvements * Fix lint * Some improvements * Fix template variables * Add permission docs * improve doc * Fix fixture * Fix bug * Fix some bug * Fix bug Co-authored-by: Lauris BH <lauris@nix.lv> Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: Aravinth Manivannan <realaravinth@batsense.net>
2 years ago
Restricted users (#6274) * Restricted users (#4334): initial implementation * Add User.IsRestricted & UI to edit it * Pass user object instead of user id to places where IsRestricted flag matters * Restricted users: maintain access rows for all referenced repos (incl public) * Take logged in user & IsRestricted flag into account in org/repo listings, searches and accesses * Add basic repo access tests for restricted users Signed-off-by: Manush Dodunekov <manush@stendahls.se> * Mention restricted users in the faq Signed-off-by: Manush Dodunekov <manush@stendahls.se> * Revert unnecessary change `.isUserPartOfOrg` -> `.IsUserPartOfOrg` Signed-off-by: Manush Dodunekov <manush@stendahls.se> * Remove unnecessary `org.IsOrganization()` call Signed-off-by: Manush Dodunekov <manush@stendahls.se> * Revert to an `int64` keyed `accessMap` * Add type `userAccess` * Add convenience func updateUserAccess() * Turn accessMap into a `map[int64]userAccess` Signed-off-by: Manush Dodunekov <manush@stendahls.se> * or even better: `map[int64]*userAccess` * updateUserAccess(): use tighter syntax as suggested by lafriks * even tighter * Avoid extra loop * Don't disclose limited orgs to unauthenticated users * Don't assume block only applies to orgs * Use an array of `VisibleType` for filtering * fix yet another thinko * Ok - no need for u * Revert "Ok - no need for u" This reverts commit 5c3e886aabd5acd997a3b35687d322439732c200. Co-authored-by: Antoine GIRARD <sapk@users.noreply.github.com> Co-authored-by: Lauris BH <lauris@nix.lv>
4 years ago
Team permission allow different unit has different permission (#17811) * Team permission allow different unit has different permission * Finish the interface and the logic * Fix lint * Fix translation * align center for table cell content * Fix fixture * merge * Fix test * Add deprecated * Improve code * Add tooltip * Fix swagger * Fix newline * Fix tests * Fix tests * Fix test * Fix test * Max permission of external wiki and issues should be read * Move team units with limited max level below units table * Update label and column names * Some improvements * Fix lint * Some improvements * Fix template variables * Add permission docs * improve doc * Fix fixture * Fix bug * Fix some bug * fix * gofumpt * Integration test for migration (#18124) integrations: basic test for Gitea {dump,restore}-repo This is a first step for integration testing of DumpRepository and RestoreRepository. It: runs a Gitea server, dumps a repo via DumpRepository to the filesystem, restores the repo via RestoreRepository from the filesystem, dumps the restored repository to the filesystem, compares the first and second dump and expects them to be identical The verification is trivial and the goal is to add more tests for each topic of the dump. Signed-off-by: Loïc Dachary <loic@dachary.org> * Team permission allow different unit has different permission * Finish the interface and the logic * Fix lint * Fix translation * align center for table cell content * Fix fixture * merge * Fix test * Add deprecated * Improve code * Add tooltip * Fix swagger * Fix newline * Fix tests * Fix tests * Fix test * Fix test * Max permission of external wiki and issues should be read * Move team units with limited max level below units table * Update label and column names * Some improvements * Fix lint * Some improvements * Fix template variables * Add permission docs * improve doc * Fix fixture * Fix bug * Fix some bug * Fix bug Co-authored-by: Lauris BH <lauris@nix.lv> Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: Aravinth Manivannan <realaravinth@batsense.net>
2 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. // Copyright 2018 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package access
  4. import (
  5. "context"
  6. "fmt"
  7. "code.gitea.io/gitea/models/db"
  8. "code.gitea.io/gitea/models/organization"
  9. perm_model "code.gitea.io/gitea/models/perm"
  10. repo_model "code.gitea.io/gitea/models/repo"
  11. "code.gitea.io/gitea/models/unit"
  12. user_model "code.gitea.io/gitea/models/user"
  13. "code.gitea.io/gitea/modules/log"
  14. )
  15. // Permission contains all the permissions related variables to a repository for a user
  16. type Permission struct {
  17. AccessMode perm_model.AccessMode
  18. Units []*repo_model.RepoUnit
  19. UnitsMode map[unit.Type]perm_model.AccessMode
  20. }
  21. // IsOwner returns true if current user is the owner of repository.
  22. func (p *Permission) IsOwner() bool {
  23. return p.AccessMode >= perm_model.AccessModeOwner
  24. }
  25. // IsAdmin returns true if current user has admin or higher access of repository.
  26. func (p *Permission) IsAdmin() bool {
  27. return p.AccessMode >= perm_model.AccessModeAdmin
  28. }
  29. // HasAccess returns true if the current user has at least read access to any unit of this repository
  30. func (p *Permission) HasAccess() bool {
  31. if p.UnitsMode == nil {
  32. return p.AccessMode >= perm_model.AccessModeRead
  33. }
  34. return len(p.UnitsMode) > 0
  35. }
  36. // UnitAccessMode returns current user accessmode to the specify unit of the repository
  37. func (p *Permission) UnitAccessMode(unitType unit.Type) perm_model.AccessMode {
  38. if p.UnitsMode == nil {
  39. for _, u := range p.Units {
  40. if u.Type == unitType {
  41. return p.AccessMode
  42. }
  43. }
  44. return perm_model.AccessModeNone
  45. }
  46. return p.UnitsMode[unitType]
  47. }
  48. // CanAccess returns true if user has mode access to the unit of the repository
  49. func (p *Permission) CanAccess(mode perm_model.AccessMode, unitType unit.Type) bool {
  50. return p.UnitAccessMode(unitType) >= mode
  51. }
  52. // CanAccessAny returns true if user has mode access to any of the units of the repository
  53. func (p *Permission) CanAccessAny(mode perm_model.AccessMode, unitTypes ...unit.Type) bool {
  54. for _, u := range unitTypes {
  55. if p.CanAccess(mode, u) {
  56. return true
  57. }
  58. }
  59. return false
  60. }
  61. // CanRead returns true if user could read to this unit
  62. func (p *Permission) CanRead(unitType unit.Type) bool {
  63. return p.CanAccess(perm_model.AccessModeRead, unitType)
  64. }
  65. // CanReadAny returns true if user has read access to any of the units of the repository
  66. func (p *Permission) CanReadAny(unitTypes ...unit.Type) bool {
  67. return p.CanAccessAny(perm_model.AccessModeRead, unitTypes...)
  68. }
  69. // CanReadIssuesOrPulls returns true if isPull is true and user could read pull requests and
  70. // returns true if isPull is false and user could read to issues
  71. func (p *Permission) CanReadIssuesOrPulls(isPull bool) bool {
  72. if isPull {
  73. return p.CanRead(unit.TypePullRequests)
  74. }
  75. return p.CanRead(unit.TypeIssues)
  76. }
  77. // CanWrite returns true if user could write to this unit
  78. func (p *Permission) CanWrite(unitType unit.Type) bool {
  79. return p.CanAccess(perm_model.AccessModeWrite, unitType)
  80. }
  81. // CanWriteIssuesOrPulls returns true if isPull is true and user could write to pull requests and
  82. // returns true if isPull is false and user could write to issues
  83. func (p *Permission) CanWriteIssuesOrPulls(isPull bool) bool {
  84. if isPull {
  85. return p.CanWrite(unit.TypePullRequests)
  86. }
  87. return p.CanWrite(unit.TypeIssues)
  88. }
  89. func (p *Permission) LogString() string {
  90. format := "<Permission AccessMode=%s, %d Units, %d UnitsMode(s): [ "
  91. args := []any{p.AccessMode.String(), len(p.Units), len(p.UnitsMode)}
  92. for i, unit := range p.Units {
  93. config := ""
  94. if unit.Config != nil {
  95. configBytes, err := unit.Config.ToDB()
  96. config = string(configBytes)
  97. if err != nil {
  98. config = err.Error()
  99. }
  100. }
  101. format += "\nUnits[%d]: ID: %d RepoID: %d Type: %s Config: %s"
  102. args = append(args, i, unit.ID, unit.RepoID, unit.Type.LogString(), config)
  103. }
  104. for key, value := range p.UnitsMode {
  105. format += "\nUnitMode[%-v]: %-v"
  106. args = append(args, key.LogString(), value.LogString())
  107. }
  108. format += " ]>"
  109. return fmt.Sprintf(format, args...)
  110. }
  111. // GetUserRepoPermission returns the user permissions to the repository
  112. func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, user *user_model.User) (Permission, error) {
  113. var perm Permission
  114. if log.IsTrace() {
  115. defer func() {
  116. if user == nil {
  117. log.Trace("Permission Loaded for anonymous user in %-v:\nPermissions: %-+v",
  118. repo,
  119. perm)
  120. return
  121. }
  122. log.Trace("Permission Loaded for %-v in %-v:\nPermissions: %-+v",
  123. user,
  124. repo,
  125. perm)
  126. }()
  127. }
  128. // anonymous user visit private repo.
  129. // TODO: anonymous user visit public unit of private repo???
  130. if user == nil && repo.IsPrivate {
  131. perm.AccessMode = perm_model.AccessModeNone
  132. return perm, nil
  133. }
  134. var isCollaborator bool
  135. var err error
  136. if user != nil {
  137. isCollaborator, err = repo_model.IsCollaborator(ctx, repo.ID, user.ID)
  138. if err != nil {
  139. return perm, err
  140. }
  141. }
  142. if err := repo.LoadOwner(ctx); err != nil {
  143. return perm, err
  144. }
  145. // Prevent strangers from checking out public repo of private organization/users
  146. // Allow user if they are collaborator of a repo within a private user or a private organization but not a member of the organization itself
  147. if !organization.HasOrgOrUserVisible(ctx, repo.Owner, user) && !isCollaborator {
  148. perm.AccessMode = perm_model.AccessModeNone
  149. return perm, nil
  150. }
  151. if err := repo.LoadUnits(ctx); err != nil {
  152. return perm, err
  153. }
  154. perm.Units = repo.Units
  155. // anonymous visit public repo
  156. if user == nil {
  157. perm.AccessMode = perm_model.AccessModeRead
  158. return perm, nil
  159. }
  160. // Admin or the owner has super access to the repository
  161. if user.IsAdmin || user.ID == repo.OwnerID {
  162. perm.AccessMode = perm_model.AccessModeOwner
  163. return perm, nil
  164. }
  165. // plain user
  166. perm.AccessMode, err = accessLevel(ctx, user, repo)
  167. if err != nil {
  168. return perm, err
  169. }
  170. if err := repo.LoadOwner(ctx); err != nil {
  171. return perm, err
  172. }
  173. if !repo.Owner.IsOrganization() {
  174. return perm, nil
  175. }
  176. perm.UnitsMode = make(map[unit.Type]perm_model.AccessMode)
  177. // Collaborators on organization
  178. if isCollaborator {
  179. for _, u := range repo.Units {
  180. perm.UnitsMode[u.Type] = perm.AccessMode
  181. }
  182. }
  183. // get units mode from teams
  184. teams, err := organization.GetUserRepoTeams(ctx, repo.OwnerID, user.ID, repo.ID)
  185. if err != nil {
  186. return perm, err
  187. }
  188. // if user in an owner team
  189. for _, team := range teams {
  190. if team.AccessMode >= perm_model.AccessModeAdmin {
  191. perm.AccessMode = perm_model.AccessModeOwner
  192. perm.UnitsMode = nil
  193. return perm, nil
  194. }
  195. }
  196. for _, u := range repo.Units {
  197. var found bool
  198. for _, team := range teams {
  199. teamMode := team.UnitAccessMode(ctx, u.Type)
  200. if teamMode > perm_model.AccessModeNone {
  201. m := perm.UnitsMode[u.Type]
  202. if m < teamMode {
  203. perm.UnitsMode[u.Type] = teamMode
  204. }
  205. found = true
  206. }
  207. }
  208. // for a public repo on an organization, a non-restricted user has read permission on non-team defined units.
  209. if !found && !repo.IsPrivate && !user.IsRestricted {
  210. if _, ok := perm.UnitsMode[u.Type]; !ok {
  211. perm.UnitsMode[u.Type] = perm_model.AccessModeRead
  212. }
  213. }
  214. }
  215. // remove no permission units
  216. perm.Units = make([]*repo_model.RepoUnit, 0, len(repo.Units))
  217. for t := range perm.UnitsMode {
  218. for _, u := range repo.Units {
  219. if u.Type == t {
  220. perm.Units = append(perm.Units, u)
  221. }
  222. }
  223. }
  224. return perm, err
  225. }
  226. // IsUserRealRepoAdmin check if this user is real repo admin
  227. func IsUserRealRepoAdmin(repo *repo_model.Repository, user *user_model.User) (bool, error) {
  228. if repo.OwnerID == user.ID {
  229. return true, nil
  230. }
  231. if err := repo.LoadOwner(db.DefaultContext); err != nil {
  232. return false, err
  233. }
  234. accessMode, err := accessLevel(db.DefaultContext, user, repo)
  235. if err != nil {
  236. return false, err
  237. }
  238. return accessMode >= perm_model.AccessModeAdmin, nil
  239. }
  240. // IsUserRepoAdmin return true if user has admin right of a repo
  241. func IsUserRepoAdmin(ctx context.Context, repo *repo_model.Repository, user *user_model.User) (bool, error) {
  242. if user == nil || repo == nil {
  243. return false, nil
  244. }
  245. if user.IsAdmin {
  246. return true, nil
  247. }
  248. mode, err := accessLevel(ctx, user, repo)
  249. if err != nil {
  250. return false, err
  251. }
  252. if mode >= perm_model.AccessModeAdmin {
  253. return true, nil
  254. }
  255. teams, err := organization.GetUserRepoTeams(ctx, repo.OwnerID, user.ID, repo.ID)
  256. if err != nil {
  257. return false, err
  258. }
  259. for _, team := range teams {
  260. if team.AccessMode >= perm_model.AccessModeAdmin {
  261. return true, nil
  262. }
  263. }
  264. return false, nil
  265. }
  266. // AccessLevel returns the Access a user has to a repository. Will return NoneAccess if the
  267. // user does not have access.
  268. func AccessLevel(ctx context.Context, user *user_model.User, repo *repo_model.Repository) (perm_model.AccessMode, error) { //nolint
  269. return AccessLevelUnit(ctx, user, repo, unit.TypeCode)
  270. }
  271. // AccessLevelUnit returns the Access a user has to a repository's. Will return NoneAccess if the
  272. // user does not have access.
  273. func AccessLevelUnit(ctx context.Context, user *user_model.User, repo *repo_model.Repository, unitType unit.Type) (perm_model.AccessMode, error) { //nolint
  274. perm, err := GetUserRepoPermission(ctx, repo, user)
  275. if err != nil {
  276. return perm_model.AccessModeNone, err
  277. }
  278. return perm.UnitAccessMode(unitType), nil
  279. }
  280. // HasAccessUnit returns true if user has testMode to the unit of the repository
  281. func HasAccessUnit(ctx context.Context, user *user_model.User, repo *repo_model.Repository, unitType unit.Type, testMode perm_model.AccessMode) (bool, error) {
  282. mode, err := AccessLevelUnit(ctx, user, repo, unitType)
  283. return testMode <= mode, err
  284. }
  285. // CanBeAssigned return true if user can be assigned to issue or pull requests in repo
  286. // Currently any write access (code, issues or pr's) is assignable, to match assignee list in user interface.
  287. func CanBeAssigned(ctx context.Context, user *user_model.User, repo *repo_model.Repository, _ bool) (bool, error) {
  288. if user.IsOrganization() {
  289. return false, fmt.Errorf("Organization can't be added as assignee [user_id: %d, repo_id: %d]", user.ID, repo.ID)
  290. }
  291. perm, err := GetUserRepoPermission(ctx, repo, user)
  292. if err != nil {
  293. return false, err
  294. }
  295. return perm.CanAccessAny(perm_model.AccessModeWrite, unit.AllRepoUnitTypes...) ||
  296. perm.CanAccessAny(perm_model.AccessModeRead, unit.TypePullRequests), nil
  297. }
  298. // HasAccess returns true if user has access to repo
  299. func HasAccess(ctx context.Context, userID int64, repo *repo_model.Repository) (bool, error) {
  300. var user *user_model.User
  301. var err error
  302. if userID > 0 {
  303. user, err = user_model.GetUserByID(ctx, userID)
  304. if err != nil {
  305. return false, err
  306. }
  307. }
  308. perm, err := GetUserRepoPermission(ctx, repo, user)
  309. if err != nil {
  310. return false, err
  311. }
  312. return perm.HasAccess(), nil
  313. }
  314. // getUsersWithAccessMode returns users that have at least given access mode to the repository.
  315. func getUsersWithAccessMode(ctx context.Context, repo *repo_model.Repository, mode perm_model.AccessMode) (_ []*user_model.User, err error) {
  316. if err = repo.LoadOwner(ctx); err != nil {
  317. return nil, err
  318. }
  319. e := db.GetEngine(ctx)
  320. accesses := make([]*Access, 0, 10)
  321. if err = e.Where("repo_id = ? AND mode >= ?", repo.ID, mode).Find(&accesses); err != nil {
  322. return nil, err
  323. }
  324. // Leave a seat for owner itself to append later, but if owner is an organization
  325. // and just waste 1 unit is cheaper than re-allocate memory once.
  326. users := make([]*user_model.User, 0, len(accesses)+1)
  327. if len(accesses) > 0 {
  328. userIDs := make([]int64, len(accesses))
  329. for i := 0; i < len(accesses); i++ {
  330. userIDs[i] = accesses[i].UserID
  331. }
  332. if err = e.In("id", userIDs).Find(&users); err != nil {
  333. return nil, err
  334. }
  335. }
  336. if !repo.Owner.IsOrganization() {
  337. users = append(users, repo.Owner)
  338. }
  339. return users, nil
  340. }
  341. // GetRepoReaders returns all users that have explicit read access or higher to the repository.
  342. func GetRepoReaders(repo *repo_model.Repository) (_ []*user_model.User, err error) {
  343. return getUsersWithAccessMode(db.DefaultContext, repo, perm_model.AccessModeRead)
  344. }
  345. // GetRepoWriters returns all users that have write access to the repository.
  346. func GetRepoWriters(repo *repo_model.Repository) (_ []*user_model.User, err error) {
  347. return getUsersWithAccessMode(db.DefaultContext, repo, perm_model.AccessModeWrite)
  348. }
  349. // IsRepoReader returns true if user has explicit read access or higher to the repository.
  350. func IsRepoReader(ctx context.Context, repo *repo_model.Repository, userID int64) (bool, error) {
  351. if repo.OwnerID == userID {
  352. return true, nil
  353. }
  354. return db.GetEngine(ctx).Where("repo_id = ? AND user_id = ? AND mode >= ?", repo.ID, userID, perm_model.AccessModeRead).Get(&Access{})
  355. }
  356. // CheckRepoUnitUser check whether user could visit the unit of this repository
  357. func CheckRepoUnitUser(ctx context.Context, repo *repo_model.Repository, user *user_model.User, unitType unit.Type) bool {
  358. if user != nil && user.IsAdmin {
  359. return true
  360. }
  361. perm, err := GetUserRepoPermission(ctx, repo, user)
  362. if err != nil {
  363. log.Error("GetUserRepoPermission: %w", err)
  364. return false
  365. }
  366. return perm.CanRead(unitType)
  367. }