summaryrefslogtreecommitdiffstats
path: root/models/repo_permission.go
diff options
context:
space:
mode:
authorLunny Xiao <xiaolunwen@gmail.com>2018-11-28 19:26:14 +0800
committerGitHub <noreply@github.com>2018-11-28 19:26:14 +0800
commiteabbddcd98717ef20d8475e819f403c50f4a9787 (patch)
treeefc525e7ec60d56d3bec72019febfa088a128b89 /models/repo_permission.go
parent0222623be9fa4a56d870213f77b92139cefc2518 (diff)
downloadgitea-eabbddcd98717ef20d8475e819f403c50f4a9787.tar.gz
gitea-eabbddcd98717ef20d8475e819f403c50f4a9787.zip
Restrict permission check on repositories and fix some problems (#5314)
* fix units permission problems * fix some bugs and merge LoadUnits to repoAssignment * refactor permission struct and add some copyright heads * remove unused codes * fix routes units check * improve permission check * add unit tests for permission * fix typo * fix tests * fix some routes * fix api permission check * improve permission check * fix some permission check * fix tests * fix tests * improve some permission check * fix some permission check * refactor AccessLevel * fix bug * fix tests * fix tests * fix tests * fix AccessLevel * rename CanAccess * fix tests * fix comment * fix bug * add missing unit for test repos * fix bug * rename some functions * fix routes check
Diffstat (limited to 'models/repo_permission.go')
-rw-r--r--models/repo_permission.go270
1 files changed, 270 insertions, 0 deletions
diff --git a/models/repo_permission.go b/models/repo_permission.go
new file mode 100644
index 0000000000..9dd7cc559d
--- /dev/null
+++ b/models/repo_permission.go
@@ -0,0 +1,270 @@
+// Copyright 2018 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package models
+
+// Permission contains all the permissions related variables to a repository for a user
+type Permission struct {
+ AccessMode AccessMode
+ Units []*RepoUnit
+ UnitsMode map[UnitType]AccessMode
+}
+
+// IsOwner returns true if current user is the owner of repository.
+func (p *Permission) IsOwner() bool {
+ return p.AccessMode >= AccessModeOwner
+}
+
+// IsAdmin returns true if current user has admin or higher access of repository.
+func (p *Permission) IsAdmin() bool {
+ return p.AccessMode >= AccessModeAdmin
+}
+
+// HasAccess returns true if the current user has at least read access to any unit of this repository
+func (p *Permission) HasAccess() bool {
+ if p.UnitsMode == nil {
+ return p.AccessMode >= AccessModeRead
+ }
+ return len(p.UnitsMode) > 0
+}
+
+// UnitAccessMode returns current user accessmode to the specify unit of the repository
+func (p *Permission) UnitAccessMode(unitType UnitType) AccessMode {
+ if p.UnitsMode == nil {
+ for _, u := range p.Units {
+ if u.Type == unitType {
+ return p.AccessMode
+ }
+ }
+ return AccessModeNone
+ }
+ return p.UnitsMode[unitType]
+}
+
+// CanAccess returns true if user has mode access to the unit of the repository
+func (p *Permission) CanAccess(mode AccessMode, unitType UnitType) bool {
+ return p.UnitAccessMode(unitType) >= mode
+}
+
+// CanAccessAny returns true if user has mode access to any of the units of the repository
+func (p *Permission) CanAccessAny(mode AccessMode, unitTypes ...UnitType) bool {
+ for _, u := range unitTypes {
+ if p.CanAccess(mode, u) {
+ return true
+ }
+ }
+ return false
+}
+
+// CanRead returns true if user could read to this unit
+func (p *Permission) CanRead(unitType UnitType) bool {
+ return p.CanAccess(AccessModeRead, unitType)
+}
+
+// CanReadAny returns true if user has read access to any of the units of the repository
+func (p *Permission) CanReadAny(unitTypes ...UnitType) bool {
+ return p.CanAccessAny(AccessModeRead, unitTypes...)
+}
+
+// CanReadIssuesOrPulls returns true if isPull is true and user could read pull requests and
+// returns true if isPull is false and user could read to issues
+func (p *Permission) CanReadIssuesOrPulls(isPull bool) bool {
+ if isPull {
+ return p.CanRead(UnitTypePullRequests)
+ }
+ return p.CanRead(UnitTypeIssues)
+}
+
+// CanWrite returns true if user could write to this unit
+func (p *Permission) CanWrite(unitType UnitType) bool {
+ return p.CanAccess(AccessModeWrite, unitType)
+}
+
+// CanWriteIssuesOrPulls returns true if isPull is true and user could write to pull requests and
+// returns true if isPull is false and user could write to issues
+func (p *Permission) CanWriteIssuesOrPulls(isPull bool) bool {
+ if isPull {
+ return p.CanWrite(UnitTypePullRequests)
+ }
+ return p.CanWrite(UnitTypeIssues)
+}
+
+// GetUserRepoPermission returns the user permissions to the repository
+func GetUserRepoPermission(repo *Repository, user *User) (Permission, error) {
+ return getUserRepoPermission(x, repo, user)
+}
+
+func getUserRepoPermission(e Engine, repo *Repository, user *User) (perm Permission, err error) {
+ // anonymous user visit private repo.
+ // TODO: anonymous user visit public unit of private repo???
+ if user == nil && repo.IsPrivate {
+ perm.AccessMode = AccessModeNone
+ return
+ }
+
+ if err = repo.getUnits(e); err != nil {
+ return
+ }
+
+ perm.Units = repo.Units
+
+ // anonymous visit public repo
+ if user == nil {
+ perm.AccessMode = AccessModeRead
+ return
+ }
+
+ // Admin or the owner has super access to the repository
+ if user.IsAdmin || user.ID == repo.OwnerID {
+ perm.AccessMode = AccessModeOwner
+ return
+ }
+
+ // plain user
+ perm.AccessMode, err = accessLevel(e, user.ID, repo)
+ if err != nil {
+ return
+ }
+
+ if err = repo.getOwner(e); err != nil {
+ return
+ }
+ if !repo.Owner.IsOrganization() {
+ return
+ }
+
+ perm.UnitsMode = make(map[UnitType]AccessMode)
+
+ // Collaborators on organization
+ if isCollaborator, err := repo.isCollaborator(e, user.ID); err != nil {
+ return perm, err
+ } else if isCollaborator {
+ for _, u := range repo.Units {
+ perm.UnitsMode[u.Type] = perm.AccessMode
+ }
+ }
+
+ // get units mode from teams
+ teams, err := getUserRepoTeams(e, repo.OwnerID, user.ID, repo.ID)
+ if err != nil {
+ return
+ }
+
+ for _, u := range repo.Units {
+ var found bool
+ for _, team := range teams {
+ if team.unitEnabled(e, u.Type) {
+ m := perm.UnitsMode[u.Type]
+ if m < team.Authorize {
+ perm.UnitsMode[u.Type] = team.Authorize
+ }
+ found = true
+ }
+ }
+
+ // for a public repo on an organization, user have read permission on non-team defined units.
+ if !found && !repo.IsPrivate {
+ if _, ok := perm.UnitsMode[u.Type]; !ok {
+ perm.UnitsMode[u.Type] = AccessModeRead
+ }
+ }
+ }
+
+ // remove no permission units
+ perm.Units = make([]*RepoUnit, 0, len(repo.Units))
+ for t := range perm.UnitsMode {
+ for _, u := range repo.Units {
+ if u.Type == t {
+ perm.Units = append(perm.Units, u)
+ }
+ }
+ }
+
+ return
+}
+
+// IsUserRepoAdmin return ture if user has admin right of a repo
+func IsUserRepoAdmin(repo *Repository, user *User) (bool, error) {
+ return isUserRepoAdmin(x, repo, user)
+}
+
+func isUserRepoAdmin(e Engine, repo *Repository, user *User) (bool, error) {
+ if user == nil || repo == nil {
+ return false, nil
+ }
+ if user.IsAdmin {
+ return true, nil
+ }
+
+ mode, err := accessLevel(e, user.ID, repo)
+ if err != nil {
+ return false, err
+ }
+ if mode >= AccessModeAdmin {
+ return true, nil
+ }
+
+ teams, err := getUserRepoTeams(e, repo.OwnerID, user.ID, repo.ID)
+ if err != nil {
+ return false, err
+ }
+
+ for _, team := range teams {
+ if team.Authorize >= AccessModeAdmin {
+ return true, nil
+ }
+ }
+ return false, nil
+}
+
+// AccessLevel returns the Access a user has to a repository. Will return NoneAccess if the
+// user does not have access.
+func AccessLevel(user *User, repo *Repository) (AccessMode, error) {
+ return accessLevelUnit(x, user, repo, UnitTypeCode)
+}
+
+func accessLevelUnit(e Engine, user *User, repo *Repository, unitType UnitType) (AccessMode, error) {
+ perm, err := getUserRepoPermission(e, repo, user)
+ if err != nil {
+ return AccessModeNone, err
+ }
+ return perm.UnitAccessMode(UnitTypeCode), nil
+}
+
+func hasAccessUnit(e Engine, user *User, repo *Repository, unitType UnitType, testMode AccessMode) (bool, error) {
+ mode, err := accessLevelUnit(e, user, repo, unitType)
+ return testMode <= mode, err
+}
+
+// HasAccessUnit returns ture if user has testMode to the unit of the repository
+func HasAccessUnit(user *User, repo *Repository, unitType UnitType, testMode AccessMode) (bool, error) {
+ return hasAccessUnit(x, user, repo, unitType, testMode)
+}
+
+// canBeAssigned return true if user could be assigned to a repo
+// FIXME: user could send PullRequest also could be assigned???
+func canBeAssigned(e Engine, user *User, repo *Repository) (bool, error) {
+ return hasAccessUnit(e, user, repo, UnitTypeCode, AccessModeWrite)
+}
+
+func hasAccess(e Engine, userID int64, repo *Repository) (bool, error) {
+ var user *User
+ var err error
+ if userID > 0 {
+ user, err = getUserByID(e, userID)
+ if err != nil {
+ return false, err
+ }
+ }
+ perm, err := getUserRepoPermission(e, repo, user)
+ if err != nil {
+ return false, err
+ }
+ return perm.HasAccess(), nil
+}
+
+// HasAccess returns true if user has access to repo
+func HasAccess(userID int64, repo *Repository) (bool, error) {
+ return hasAccess(x, userID, repo)
+}