diff options
author | Lunny Xiao <xiaolunwen@gmail.com> | 2018-11-28 19:26:14 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-11-28 19:26:14 +0800 |
commit | eabbddcd98717ef20d8475e819f403c50f4a9787 (patch) | |
tree | efc525e7ec60d56d3bec72019febfa088a128b89 /models/repo_permission.go | |
parent | 0222623be9fa4a56d870213f77b92139cefc2518 (diff) | |
download | gitea-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.go | 270 |
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) +} |