mirror of
https://github.com/go-gitea/gitea.git
synced 2025-06-22 05:58:02 +02:00
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
This commit is contained in:
64
modules/context/permission.go
Normal file
64
modules/context/permission.go
Normal file
@ -0,0 +1,64 @@
|
||||
// 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 context
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/models"
|
||||
macaron "gopkg.in/macaron.v1"
|
||||
)
|
||||
|
||||
// RequireRepoAdmin returns a macaron middleware for requiring repository admin permission
|
||||
func RequireRepoAdmin() macaron.Handler {
|
||||
return func(ctx *Context) {
|
||||
if !ctx.IsSigned || !ctx.Repo.IsAdmin() {
|
||||
ctx.NotFound(ctx.Req.RequestURI, nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RequireRepoWriter returns a macaron middleware for requiring repository write to the specify unitType
|
||||
func RequireRepoWriter(unitType models.UnitType) macaron.Handler {
|
||||
return func(ctx *Context) {
|
||||
if !ctx.Repo.CanWrite(unitType) {
|
||||
ctx.NotFound(ctx.Req.RequestURI, nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RequireRepoWriterOr returns a macaron middleware for requiring repository write to one of the unit permission
|
||||
func RequireRepoWriterOr(unitTypes ...models.UnitType) macaron.Handler {
|
||||
return func(ctx *Context) {
|
||||
for _, unitType := range unitTypes {
|
||||
if ctx.Repo.CanWrite(unitType) {
|
||||
return
|
||||
}
|
||||
}
|
||||
ctx.NotFound(ctx.Req.RequestURI, nil)
|
||||
}
|
||||
}
|
||||
|
||||
// RequireRepoReader returns a macaron middleware for requiring repository read to the specify unitType
|
||||
func RequireRepoReader(unitType models.UnitType) macaron.Handler {
|
||||
return func(ctx *Context) {
|
||||
if !ctx.Repo.CanRead(unitType) {
|
||||
ctx.NotFound(ctx.Req.RequestURI, nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RequireRepoReaderOr returns a macaron middleware for requiring repository write to one of the unit permission
|
||||
func RequireRepoReaderOr(unitTypes ...models.UnitType) macaron.Handler {
|
||||
return func(ctx *Context) {
|
||||
for _, unitType := range unitTypes {
|
||||
if ctx.Repo.CanRead(unitType) {
|
||||
return
|
||||
}
|
||||
}
|
||||
ctx.NotFound(ctx.Req.RequestURI, nil)
|
||||
}
|
||||
}
|
@ -32,7 +32,7 @@ type PullRequest struct {
|
||||
|
||||
// Repository contains information to operate a repository
|
||||
type Repository struct {
|
||||
AccessMode models.AccessMode
|
||||
models.Permission
|
||||
IsWatching bool
|
||||
IsViewBranch bool
|
||||
IsViewTag bool
|
||||
@ -54,34 +54,14 @@ type Repository struct {
|
||||
PullRequest *PullRequest
|
||||
}
|
||||
|
||||
// IsOwner returns true if current user is the owner of repository.
|
||||
func (r *Repository) IsOwner() bool {
|
||||
return r.AccessMode >= models.AccessModeOwner
|
||||
}
|
||||
|
||||
// IsAdmin returns true if current user has admin or higher access of repository.
|
||||
func (r *Repository) IsAdmin() bool {
|
||||
return r.AccessMode >= models.AccessModeAdmin
|
||||
}
|
||||
|
||||
// IsWriter returns true if current user has write or higher access of repository.
|
||||
func (r *Repository) IsWriter() bool {
|
||||
return r.AccessMode >= models.AccessModeWrite
|
||||
}
|
||||
|
||||
// HasAccess returns true if the current user has at least read access for this repository
|
||||
func (r *Repository) HasAccess() bool {
|
||||
return r.AccessMode >= models.AccessModeRead
|
||||
}
|
||||
|
||||
// CanEnableEditor returns true if repository is editable and user has proper access level.
|
||||
func (r *Repository) CanEnableEditor() bool {
|
||||
return r.Repository.CanEnableEditor() && r.IsViewBranch && r.IsWriter()
|
||||
return r.Permission.CanWrite(models.UnitTypeCode) && r.Repository.CanEnableEditor() && r.IsViewBranch
|
||||
}
|
||||
|
||||
// CanCreateBranch returns true if repository is editable and user has proper access level.
|
||||
func (r *Repository) CanCreateBranch() bool {
|
||||
return r.Repository.CanCreateBranch() && r.IsWriter()
|
||||
return r.Permission.CanWrite(models.UnitTypeCode) && r.Repository.CanCreateBranch()
|
||||
}
|
||||
|
||||
// CanCommitToBranch returns true if repository is editable and user has proper access level
|
||||
@ -101,12 +81,12 @@ func (r *Repository) CanUseTimetracker(issue *models.Issue, user *models.User) b
|
||||
// 2. Is the user a contributor, admin, poster or assignee and do the repository policies require this?
|
||||
isAssigned, _ := models.IsUserAssignedToIssue(issue, user)
|
||||
return r.Repository.IsTimetrackerEnabled() && (!r.Repository.AllowOnlyContributorsToTrackTime() ||
|
||||
r.IsWriter() || issue.IsPoster(user.ID) || isAssigned)
|
||||
r.Permission.CanWrite(models.UnitTypeIssues) || issue.IsPoster(user.ID) || isAssigned)
|
||||
}
|
||||
|
||||
// CanCreateIssueDependencies returns whether or not a user can create dependencies.
|
||||
func (r *Repository) CanCreateIssueDependencies(user *models.User) bool {
|
||||
return r.Repository.IsDependenciesEnabled() && r.IsWriter()
|
||||
return r.Permission.CanWrite(models.UnitTypeIssues) && r.Repository.IsDependenciesEnabled()
|
||||
}
|
||||
|
||||
// GetCommitsCount returns cached commit count for current view
|
||||
@ -221,24 +201,15 @@ func RedirectToRepo(ctx *Context, redirectRepoID int64) {
|
||||
}
|
||||
|
||||
func repoAssignment(ctx *Context, repo *models.Repository) {
|
||||
// Admin has super access.
|
||||
if ctx.IsSigned && ctx.User.IsAdmin {
|
||||
ctx.Repo.AccessMode = models.AccessModeOwner
|
||||
} else {
|
||||
var userID int64
|
||||
if ctx.User != nil {
|
||||
userID = ctx.User.ID
|
||||
}
|
||||
mode, err := models.AccessLevel(userID, repo)
|
||||
if err != nil {
|
||||
ctx.ServerError("AccessLevel", err)
|
||||
return
|
||||
}
|
||||
ctx.Repo.AccessMode = mode
|
||||
var err error
|
||||
ctx.Repo.Permission, err = models.GetUserRepoPermission(repo, ctx.User)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetUserRepoPermission", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Check access.
|
||||
if ctx.Repo.AccessMode == models.AccessModeNone {
|
||||
if ctx.Repo.Permission.AccessMode == models.AccessModeNone {
|
||||
if ctx.Query("go-get") == "1" {
|
||||
EarlyResponseForGoGetMeta(ctx)
|
||||
return
|
||||
@ -247,6 +218,7 @@ func repoAssignment(ctx *Context, repo *models.Repository) {
|
||||
return
|
||||
}
|
||||
ctx.Data["HasAccess"] = true
|
||||
ctx.Data["Permission"] = &ctx.Repo.Permission
|
||||
|
||||
if repo.IsMirror {
|
||||
var err error
|
||||
@ -281,10 +253,6 @@ func RepoIDAssignment() macaron.Handler {
|
||||
return
|
||||
}
|
||||
|
||||
if err = repo.GetOwner(); err != nil {
|
||||
ctx.ServerError("GetOwner", err)
|
||||
return
|
||||
}
|
||||
repoAssignment(ctx, repo)
|
||||
}
|
||||
}
|
||||
@ -381,7 +349,9 @@ func RepoAssignment() macaron.Handler {
|
||||
ctx.Data["Owner"] = ctx.Repo.Repository.Owner
|
||||
ctx.Data["IsRepositoryOwner"] = ctx.Repo.IsOwner()
|
||||
ctx.Data["IsRepositoryAdmin"] = ctx.Repo.IsAdmin()
|
||||
ctx.Data["IsRepositoryWriter"] = ctx.Repo.IsWriter()
|
||||
ctx.Data["CanWriteCode"] = ctx.Repo.CanWrite(models.UnitTypeCode)
|
||||
ctx.Data["CanWriteIssues"] = ctx.Repo.CanWrite(models.UnitTypeIssues)
|
||||
ctx.Data["CanWritePulls"] = ctx.Repo.CanWrite(models.UnitTypePullRequests)
|
||||
|
||||
if ctx.Data["CanSignedUserFork"], err = ctx.Repo.Repository.CanUserFork(ctx.User); err != nil {
|
||||
ctx.ServerError("CanUserFork", err)
|
||||
@ -435,7 +405,7 @@ func RepoAssignment() macaron.Handler {
|
||||
}
|
||||
|
||||
// People who have push access or have forked repository can propose a new pull request.
|
||||
if ctx.Repo.IsWriter() || (ctx.IsSigned && ctx.User.HasForkedRepo(ctx.Repo.Repository.ID)) {
|
||||
if ctx.Repo.CanWrite(models.UnitTypeCode) || (ctx.IsSigned && ctx.User.HasForkedRepo(ctx.Repo.Repository.ID)) {
|
||||
// Pull request is allowed if this is a fork repository
|
||||
// and base repository accepts pull requests.
|
||||
if repo.BaseRepo != nil && repo.BaseRepo.AllowsPulls() {
|
||||
@ -453,9 +423,6 @@ func RepoAssignment() macaron.Handler {
|
||||
ctx.Repo.PullRequest.HeadInfo = ctx.Repo.BranchName
|
||||
}
|
||||
}
|
||||
|
||||
// Reset repo units as otherwise user specific units wont be loaded later
|
||||
ctx.Repo.Repository.Units = nil
|
||||
}
|
||||
ctx.Data["PullRequestCtx"] = ctx.Repo.PullRequest
|
||||
|
||||
@ -661,64 +628,6 @@ func RepoRefByType(refType RepoRefType) macaron.Handler {
|
||||
}
|
||||
}
|
||||
|
||||
// RequireRepoAdmin returns a macaron middleware for requiring repository admin permission
|
||||
func RequireRepoAdmin() macaron.Handler {
|
||||
return func(ctx *Context) {
|
||||
if !ctx.IsSigned || (!ctx.Repo.IsAdmin() && !ctx.User.IsAdmin) {
|
||||
ctx.NotFound(ctx.Req.RequestURI, nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RequireRepoWriter returns a macaron middleware for requiring repository write permission
|
||||
func RequireRepoWriter() macaron.Handler {
|
||||
return func(ctx *Context) {
|
||||
if !ctx.IsSigned || (!ctx.Repo.IsWriter() && !ctx.User.IsAdmin) {
|
||||
ctx.NotFound(ctx.Req.RequestURI, nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// LoadRepoUnits loads repsitory's units, it should be called after repository and user loaded
|
||||
func LoadRepoUnits() macaron.Handler {
|
||||
return func(ctx *Context) {
|
||||
var isAdmin bool
|
||||
if ctx.User != nil && ctx.User.IsAdmin {
|
||||
isAdmin = true
|
||||
}
|
||||
|
||||
var userID int64
|
||||
if ctx.User != nil {
|
||||
userID = ctx.User.ID
|
||||
}
|
||||
err := ctx.Repo.Repository.LoadUnitsByUserID(userID, isAdmin)
|
||||
if err != nil {
|
||||
ctx.ServerError("LoadUnitsByUserID", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CheckUnit will check whether unit type is enabled
|
||||
func CheckUnit(unitType models.UnitType) macaron.Handler {
|
||||
return func(ctx *Context) {
|
||||
if !ctx.Repo.Repository.UnitEnabled(unitType) {
|
||||
ctx.NotFound("CheckUnit", fmt.Errorf("%s: %v", ctx.Tr("units.error.unit_not_allowed"), unitType))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CheckAnyUnit will check whether any of the unit types are enabled
|
||||
func CheckAnyUnit(unitTypes ...models.UnitType) macaron.Handler {
|
||||
return func(ctx *Context) {
|
||||
if !ctx.Repo.Repository.AnyUnitEnabled(unitTypes...) {
|
||||
ctx.NotFound("CheckAnyUnit", fmt.Errorf("%s: %v", ctx.Tr("units.error.unit_not_allowed"), unitTypes))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GitHookService checks if repository Git hooks service has been enabled.
|
||||
func GitHookService() macaron.Handler {
|
||||
return func(ctx *Context) {
|
||||
|
@ -497,12 +497,12 @@ func authenticate(ctx *context.Context, repository *models.Repository, authoriza
|
||||
accessMode = models.AccessModeWrite
|
||||
}
|
||||
|
||||
if !repository.IsPrivate && !requireWrite {
|
||||
return true
|
||||
perm, err := models.GetUserRepoPermission(repository, ctx.User)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if ctx.IsSigned {
|
||||
accessCheck, _ := models.HasAccess(ctx.User.ID, repository, accessMode)
|
||||
return accessCheck
|
||||
return perm.CanAccess(accessMode, models.UnitTypeCode)
|
||||
}
|
||||
|
||||
user, repo, opStr, err := parseToken(authorization)
|
||||
@ -511,8 +511,11 @@ func authenticate(ctx *context.Context, repository *models.Repository, authoriza
|
||||
}
|
||||
ctx.User = user
|
||||
if opStr == "basic" {
|
||||
accessCheck, _ := models.HasAccess(ctx.User.ID, repository, accessMode)
|
||||
return accessCheck
|
||||
perm, err = models.GetUserRepoPermission(repository, ctx.User)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return perm.CanAccess(accessMode, models.UnitTypeCode)
|
||||
}
|
||||
if repository.ID == repo.ID {
|
||||
if requireWrite && opStr != "upload" {
|
||||
|
@ -51,27 +51,9 @@ func newInternalRequest(url, method string) *httplib.Request {
|
||||
}
|
||||
|
||||
// CheckUnitUser check whether user could visit the unit of this repository
|
||||
func CheckUnitUser(userID, repoID int64, isAdmin bool, unitType models.UnitType) (bool, error) {
|
||||
func CheckUnitUser(userID, repoID int64, isAdmin bool, unitType models.UnitType) (*models.AccessMode, error) {
|
||||
reqURL := setting.LocalURL + fmt.Sprintf("api/internal/repositories/%d/user/%d/checkunituser?isAdmin=%t&unitType=%d", repoID, userID, isAdmin, unitType)
|
||||
log.GitLogger.Trace("AccessLevel: %s", reqURL)
|
||||
|
||||
resp, err := newInternalRequest(reqURL, "GET").Response()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == 200 {
|
||||
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(userID, repoID int64) (*models.AccessMode, error) {
|
||||
reqURL := setting.LocalURL + fmt.Sprintf("api/internal/repositories/%d/user/%d/accesslevel", repoID, userID)
|
||||
log.GitLogger.Trace("AccessLevel: %s", reqURL)
|
||||
log.GitLogger.Trace("CheckUnitUser: %s", reqURL)
|
||||
|
||||
resp, err := newInternalRequest(reqURL, "GET").Response()
|
||||
if err != nil {
|
||||
@ -80,7 +62,7 @@ func AccessLevel(userID, repoID int64) (*models.AccessMode, error) {
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return nil, fmt.Errorf("Failed to get user access level: %s", decodeJSONError(resp).Err)
|
||||
return nil, fmt.Errorf("Failed to CheckUnitUser: %s", decodeJSONError(resp).Err)
|
||||
}
|
||||
|
||||
var a models.AccessMode
|
||||
|
@ -47,6 +47,9 @@ func LoadRepo(t *testing.T, ctx *context.Context, repoID int64) {
|
||||
ctx.Repo = &context.Repository{}
|
||||
ctx.Repo.Repository = models.AssertExistsAndLoadBean(t, &models.Repository{ID: repoID}).(*models.Repository)
|
||||
ctx.Repo.RepoLink = ctx.Repo.Repository.Link()
|
||||
var err error
|
||||
ctx.Repo.Permission, err = models.GetUserRepoPermission(ctx.Repo.Repository, ctx.User)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
// LoadRepoCommit loads a repo's commit into a test context.
|
||||
|
Reference in New Issue
Block a user