mirror of
				https://github.com/go-gitea/gitea
				synced 2025-10-31 11:28:24 +00: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) { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user