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 /routers | |
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 'routers')
27 files changed, 409 insertions, 364 deletions
diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 0be8f84836..47e556c881 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -1,5 +1,5 @@ // Copyright 2015 The Gogs Authors. All rights reserved. -// Copyright 2018 The Gitea Authors. All rights reserved. +// Copyright 2016 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. @@ -71,7 +71,6 @@ import ( "code.gitea.io/gitea/routers/api/v1/repo" _ "code.gitea.io/gitea/routers/api/v1/swagger" // for swagger generation "code.gitea.io/gitea/routers/api/v1/user" - "code.gitea.io/gitea/routers/api/v1/utils" api "code.gitea.io/sdk/gitea" "github.com/go-macaron/binding" @@ -152,24 +151,18 @@ func repoAssignment() macaron.Handler { return } repo.Owner = owner + ctx.Repo.Repository = repo - if ctx.IsSigned && ctx.User.IsAdmin { - ctx.Repo.AccessMode = models.AccessModeOwner - } else { - mode, err := models.AccessLevel(utils.UserID(ctx), repo) - if err != nil { - ctx.Error(500, "AccessLevel", err) - return - } - ctx.Repo.AccessMode = mode + ctx.Repo.Permission, err = models.GetUserRepoPermission(repo, ctx.User) + if err != nil { + ctx.Error(500, "GetUserRepoPermission", err) + return } if !ctx.Repo.HasAccess() { ctx.Status(404) return } - - ctx.Repo.Repository = repo } } @@ -196,7 +189,8 @@ func reqBasicAuth() macaron.Handler { } } -func reqAdmin() macaron.Handler { +// reqSiteAdmin user should be the site admin +func reqSiteAdmin() macaron.Handler { return func(ctx *context.Context) { if !ctx.IsSigned || !ctx.User.IsAdmin { ctx.Error(403) @@ -205,15 +199,56 @@ func reqAdmin() macaron.Handler { } } -func reqRepoWriter() macaron.Handler { +// reqOwner user should be the owner of the repo. +func reqOwner() macaron.Handler { + return func(ctx *context.Context) { + if !ctx.Repo.IsOwner() { + ctx.Error(403) + return + } + } +} + +// reqAdmin user should be an owner or a collaborator with admin write of a repository +func reqAdmin() macaron.Handler { + return func(ctx *context.Context) { + if !ctx.Repo.IsAdmin() { + ctx.Error(403) + return + } + } +} + +func reqRepoReader(unitType models.UnitType) macaron.Handler { + return func(ctx *context.Context) { + if !ctx.Repo.CanRead(unitType) { + ctx.Error(403) + return + } + } +} + +func reqAnyRepoReader() macaron.Handler { return func(ctx *context.Context) { - if !ctx.Repo.IsWriter() { + if !ctx.Repo.HasAccess() { ctx.Error(403) return } } } +func reqRepoWriter(unitTypes ...models.UnitType) macaron.Handler { + return func(ctx *context.Context) { + for _, unitType := range unitTypes { + if ctx.Repo.CanWrite(unitType) { + return + } + } + + ctx.Error(403) + } +} + func reqOrgMembership() macaron.Handler { return func(ctx *context.APIContext) { var orgID int64 @@ -308,22 +343,22 @@ func orgAssignment(args ...bool) macaron.Handler { } func mustEnableIssues(ctx *context.APIContext) { - if !ctx.Repo.Repository.UnitEnabled(models.UnitTypeIssues) { + if !ctx.Repo.CanRead(models.UnitTypeIssues) { ctx.Status(404) return } } func mustAllowPulls(ctx *context.Context) { - if !ctx.Repo.Repository.AllowsPulls() { + if !(ctx.Repo.Repository.CanEnablePulls() && ctx.Repo.CanRead(models.UnitTypePullRequests)) { ctx.Status(404) return } } func mustEnableIssuesOrPulls(ctx *context.Context) { - if !ctx.Repo.Repository.UnitEnabled(models.UnitTypeIssues) && - !ctx.Repo.Repository.AllowsPulls() { + if !ctx.Repo.CanRead(models.UnitTypeIssues) && + !(ctx.Repo.Repository.CanEnablePulls() && ctx.Repo.CanRead(models.UnitTypePullRequests)) { ctx.Status(404) return } @@ -443,7 +478,8 @@ func RegisterRoutes(m *macaron.Macaron) { m.Post("/migrate", reqToken(), bind(auth.MigrateRepoForm{}), repo.Migrate) m.Group("/:username/:reponame", func() { - m.Combo("").Get(repo.Get).Delete(reqToken(), repo.Delete) + m.Combo("").Get(reqAnyRepoReader(), repo.Get). + Delete(reqToken(), reqOwner(), repo.Delete) m.Group("/hooks", func() { m.Combo("").Get(repo.ListHooks). Post(bind(api.CreateHookOption{}), repo.CreateHook) @@ -453,31 +489,30 @@ func RegisterRoutes(m *macaron.Macaron) { Delete(repo.DeleteHook) m.Post("/tests", context.RepoRef(), repo.TestHook) }) - }, reqToken(), reqRepoWriter()) + }, reqToken(), reqAdmin()) m.Group("/collaborators", func() { m.Get("", repo.ListCollaborators) m.Combo("/:collaborator").Get(repo.IsCollaborator). Put(bind(api.AddCollaboratorOption{}), repo.AddCollaborator). Delete(repo.DeleteCollaborator) - }, reqToken()) - m.Get("/raw/*", context.RepoRefByType(context.RepoRefAny), repo.GetRawFile) - m.Get("/archive/*", repo.GetArchive) + }, reqToken(), reqAdmin()) + m.Get("/raw/*", context.RepoRefByType(context.RepoRefAny), reqRepoReader(models.UnitTypeCode), repo.GetRawFile) + m.Get("/archive/*", reqRepoReader(models.UnitTypeCode), repo.GetArchive) m.Combo("/forks").Get(repo.ListForks). - Post(reqToken(), bind(api.CreateForkOption{}), repo.CreateFork) + Post(reqToken(), reqRepoReader(models.UnitTypeCode), bind(api.CreateForkOption{}), repo.CreateFork) m.Group("/branches", func() { m.Get("", repo.ListBranches) m.Get("/*", context.RepoRefByType(context.RepoRefBranch), repo.GetBranch) - }) + }, reqRepoReader(models.UnitTypeCode)) m.Group("/keys", func() { m.Combo("").Get(repo.ListDeployKeys). Post(bind(api.CreateKeyOption{}), repo.CreateDeployKey) m.Combo("/:id").Get(repo.GetDeployKey). Delete(repo.DeleteDeploykey) - }, reqToken(), reqRepoWriter()) + }, reqToken(), reqAdmin()) m.Group("/times", func() { m.Combo("").Get(repo.ListTrackedTimesByRepository) m.Combo("/:timetrackingusername").Get(repo.ListTrackedTimesByUser) - }, mustEnableIssues) m.Group("/issues", func() { m.Combo("").Get(repo.ListIssues). @@ -517,17 +552,17 @@ func RegisterRoutes(m *macaron.Macaron) { }, mustEnableIssuesOrPulls) m.Group("/labels", func() { m.Combo("").Get(repo.ListLabels). - Post(reqToken(), bind(api.CreateLabelOption{}), repo.CreateLabel) + Post(reqToken(), reqRepoWriter(models.UnitTypeIssues, models.UnitTypePullRequests), bind(api.CreateLabelOption{}), repo.CreateLabel) m.Combo("/:id").Get(repo.GetLabel). - Patch(reqToken(), bind(api.EditLabelOption{}), repo.EditLabel). - Delete(reqToken(), repo.DeleteLabel) + Patch(reqToken(), reqRepoWriter(models.UnitTypeIssues, models.UnitTypePullRequests), bind(api.EditLabelOption{}), repo.EditLabel). + Delete(reqToken(), reqRepoWriter(models.UnitTypeIssues, models.UnitTypePullRequests), repo.DeleteLabel) }) m.Group("/milestones", func() { m.Combo("").Get(repo.ListMilestones). - Post(reqToken(), reqRepoWriter(), bind(api.CreateMilestoneOption{}), repo.CreateMilestone) + Post(reqToken(), reqRepoWriter(models.UnitTypeIssues, models.UnitTypePullRequests), bind(api.CreateMilestoneOption{}), repo.CreateMilestone) m.Combo("/:id").Get(repo.GetMilestone). - Patch(reqToken(), reqRepoWriter(), bind(api.EditMilestoneOption{}), repo.EditMilestone). - Delete(reqToken(), reqRepoWriter(), repo.DeleteMilestone) + Patch(reqToken(), reqRepoWriter(models.UnitTypeIssues, models.UnitTypePullRequests), bind(api.EditMilestoneOption{}), repo.EditMilestone). + Delete(reqToken(), reqRepoWriter(models.UnitTypeIssues, models.UnitTypePullRequests), repo.DeleteMilestone) }) m.Get("/stargazers", repo.ListStargazers) m.Get("/subscribers", repo.ListSubscribers) @@ -538,45 +573,44 @@ func RegisterRoutes(m *macaron.Macaron) { }) m.Group("/releases", func() { m.Combo("").Get(repo.ListReleases). - Post(reqToken(), reqRepoWriter(), context.ReferencesGitRepo(), bind(api.CreateReleaseOption{}), repo.CreateRelease) + Post(reqToken(), reqRepoWriter(models.UnitTypeReleases), context.ReferencesGitRepo(), bind(api.CreateReleaseOption{}), repo.CreateRelease) m.Group("/:id", func() { m.Combo("").Get(repo.GetRelease). - Patch(reqToken(), reqRepoWriter(), context.ReferencesGitRepo(), bind(api.EditReleaseOption{}), repo.EditRelease). - Delete(reqToken(), reqRepoWriter(), repo.DeleteRelease) + Patch(reqToken(), reqRepoWriter(models.UnitTypeReleases), context.ReferencesGitRepo(), bind(api.EditReleaseOption{}), repo.EditRelease). + Delete(reqToken(), reqRepoWriter(models.UnitTypeReleases), repo.DeleteRelease) m.Group("/assets", func() { m.Combo("").Get(repo.ListReleaseAttachments). - Post(reqToken(), reqRepoWriter(), repo.CreateReleaseAttachment) + Post(reqToken(), reqRepoWriter(models.UnitTypeReleases), repo.CreateReleaseAttachment) m.Combo("/:asset").Get(repo.GetReleaseAttachment). - Patch(reqToken(), reqRepoWriter(), bind(api.EditAttachmentOptions{}), repo.EditReleaseAttachment). - Delete(reqToken(), reqRepoWriter(), repo.DeleteReleaseAttachment) + Patch(reqToken(), reqRepoWriter(models.UnitTypeReleases), bind(api.EditAttachmentOptions{}), repo.EditReleaseAttachment). + Delete(reqToken(), reqRepoWriter(models.UnitTypeReleases), repo.DeleteReleaseAttachment) }) }) - }) - m.Post("/mirror-sync", reqToken(), reqRepoWriter(), repo.MirrorSync) - m.Get("/editorconfig/:filename", context.RepoRef(), repo.GetEditorconfig) + }, reqRepoReader(models.UnitTypeReleases)) + m.Post("/mirror-sync", reqToken(), reqRepoWriter(models.UnitTypeCode), repo.MirrorSync) + m.Get("/editorconfig/:filename", context.RepoRef(), reqRepoReader(models.UnitTypeCode), repo.GetEditorconfig) m.Group("/pulls", func() { m.Combo("").Get(bind(api.ListPullRequestsOptions{}), repo.ListPullRequests). - Post(reqToken(), reqRepoWriter(), bind(api.CreatePullRequestOption{}), repo.CreatePullRequest) + Post(reqToken(), bind(api.CreatePullRequestOption{}), repo.CreatePullRequest) m.Group("/:index", func() { m.Combo("").Get(repo.GetPullRequest). - Patch(reqToken(), reqRepoWriter(), bind(api.EditPullRequestOption{}), repo.EditPullRequest) + Patch(reqToken(), reqRepoWriter(models.UnitTypePullRequests), bind(api.EditPullRequestOption{}), repo.EditPullRequest) m.Combo("/merge").Get(repo.IsPullRequestMerged). - Post(reqToken(), reqRepoWriter(), bind(auth.MergePullRequestForm{}), repo.MergePullRequest) + Post(reqToken(), reqRepoWriter(models.UnitTypePullRequests), bind(auth.MergePullRequestForm{}), repo.MergePullRequest) }) - - }, mustAllowPulls, context.ReferencesGitRepo()) + }, mustAllowPulls, reqRepoReader(models.UnitTypeCode), context.ReferencesGitRepo()) m.Group("/statuses", func() { m.Combo("/:sha").Get(repo.GetCommitStatuses). - Post(reqToken(), reqRepoWriter(), bind(api.CreateStatusOption{}), repo.NewCommitStatus) - }) + Post(reqToken(), bind(api.CreateStatusOption{}), repo.NewCommitStatus) + }, reqRepoReader(models.UnitTypeCode)) m.Group("/commits/:ref", func() { m.Get("/status", repo.GetCombinedCommitStatusByRef) m.Get("/statuses", repo.GetCommitStatusesByRef) - }) + }, reqRepoReader(models.UnitTypeCode)) m.Group("/git", func() { m.Get("/refs", repo.GetGitAllRefs) m.Get("/refs/*", repo.GetGitRefs) - }) + }, reqRepoReader(models.UnitTypeCode)) }, repoAssignment()) }) @@ -645,7 +679,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Post("/repos", bind(api.CreateRepoOption{}), admin.CreateRepo) }) }) - }, reqToken(), reqAdmin()) + }, reqToken(), reqSiteAdmin()) m.Group("/topics", func() { m.Get("/search", repo.TopicSearch) diff --git a/routers/api/v1/org/team.go b/routers/api/v1/org/team.go index 8b67eda42f..a22d25eae3 100644 --- a/routers/api/v1/org/team.go +++ b/routers/api/v1/org/team.go @@ -311,7 +311,7 @@ func GetTeamRepos(ctx *context.APIContext) { } repos := make([]*api.Repository, len(team.Repos)) for i, repo := range team.Repos { - access, err := models.AccessLevel(ctx.User.ID, repo) + access, err := models.AccessLevel(ctx.User, repo) if err != nil { ctx.Error(500, "GetTeamRepos", err) return @@ -366,7 +366,7 @@ func AddTeamRepository(ctx *context.APIContext) { if ctx.Written() { return } - if access, err := models.AccessLevel(ctx.User.ID, repo); err != nil { + if access, err := models.AccessLevel(ctx.User, repo); err != nil { ctx.Error(500, "AccessLevel", err) return } else if access < models.AccessModeAdmin { @@ -413,7 +413,7 @@ func RemoveTeamRepository(ctx *context.APIContext) { if ctx.Written() { return } - if access, err := models.AccessLevel(ctx.User.ID, repo); err != nil { + if access, err := models.AccessLevel(ctx.User, repo); err != nil { ctx.Error(500, "AccessLevel", err) return } else if access < models.AccessModeAdmin { diff --git a/routers/api/v1/repo/collaborators.go b/routers/api/v1/repo/collaborators.go index c52472b0f8..2f254f7103 100644 --- a/routers/api/v1/repo/collaborators.go +++ b/routers/api/v1/repo/collaborators.go @@ -1,4 +1,5 @@ // Copyright 2016 The Gogs Authors. All rights reserved. +// 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. @@ -34,10 +35,6 @@ func ListCollaborators(ctx *context.APIContext) { // responses: // "200": // "$ref": "#/responses/UserList" - if !ctx.Repo.IsWriter() { - ctx.Error(403, "", "User does not have push access") - return - } collaborators, err := ctx.Repo.Repository.GetCollaborators() if err != nil { ctx.Error(500, "ListCollaborators", err) @@ -78,10 +75,6 @@ func IsCollaborator(ctx *context.APIContext) { // "$ref": "#/responses/empty" // "404": // "$ref": "#/responses/empty" - if !ctx.Repo.IsWriter() { - ctx.Error(403, "", "User does not have push access") - return - } user, err := models.GetUserByName(ctx.Params(":collaborator")) if err != nil { if models.IsErrUserNotExist(err) { @@ -133,10 +126,6 @@ func AddCollaborator(ctx *context.APIContext, form api.AddCollaboratorOption) { // responses: // "204": // "$ref": "#/responses/empty" - if !ctx.Repo.IsWriter() { - ctx.Error(403, "", "User does not have push access") - return - } collaborator, err := models.GetUserByName(ctx.Params(":collaborator")) if err != nil { if models.IsErrUserNotExist(err) { @@ -193,11 +182,6 @@ func DeleteCollaborator(ctx *context.APIContext) { // responses: // "204": // "$ref": "#/responses/empty" - if !ctx.Repo.IsWriter() { - ctx.Error(403, "", "User does not have push access") - return - } - collaborator, err := models.GetUserByName(ctx.Params(":collaborator")) if err != nil { if models.IsErrUserNotExist(err) { diff --git a/routers/api/v1/repo/file.go b/routers/api/v1/repo/file.go index 610247bc27..81f2332089 100644 --- a/routers/api/v1/repo/file.go +++ b/routers/api/v1/repo/file.go @@ -1,4 +1,5 @@ // Copyright 2014 The Gogs Authors. All rights reserved. +// 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. @@ -38,11 +39,6 @@ func GetRawFile(ctx *context.APIContext) { // responses: // 200: // description: success - if !ctx.Repo.HasAccess() { - ctx.Status(404) - return - } - if ctx.Repo.Repository.IsBare { ctx.Status(404) return diff --git a/routers/api/v1/repo/fork.go b/routers/api/v1/repo/fork.go index 843559d523..d10f668712 100644 --- a/routers/api/v1/repo/fork.go +++ b/routers/api/v1/repo/fork.go @@ -7,7 +7,6 @@ package repo import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" - "code.gitea.io/gitea/routers/api/v1/utils" api "code.gitea.io/sdk/gitea" ) @@ -40,7 +39,7 @@ func ListForks(ctx *context.APIContext) { } apiForks := make([]*api.Repository, len(forks)) for i, fork := range forks { - access, err := models.AccessLevel(utils.UserID(ctx), fork) + access, err := models.AccessLevel(ctx.User, fork) if err != nil { ctx.Error(500, "AccessLevel", err) return diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go index cd78135a62..7a8ed09b48 100644 --- a/routers/api/v1/repo/issue.go +++ b/routers/api/v1/repo/issue.go @@ -169,7 +169,7 @@ func CreateIssue(ctx *context.APIContext, form api.CreateIssueOption) { // "$ref": "#/responses/Issue" var deadlineUnix util.TimeStamp - if form.Deadline != nil && ctx.Repo.IsWriter() { + if form.Deadline != nil && ctx.Repo.CanWrite(models.UnitTypeIssues) { deadlineUnix = util.TimeStamp(form.Deadline.Unix()) } @@ -184,7 +184,7 @@ func CreateIssue(ctx *context.APIContext, form api.CreateIssueOption) { var assigneeIDs = make([]int64, 0) var err error - if ctx.Repo.IsWriter() { + if ctx.Repo.CanWrite(models.UnitTypeIssues) { issue.MilestoneID = form.Milestone assigneeIDs, err = models.MakeIDsFromAPIAssigneesToAdd(form.Assignee, form.Assignees) if err != nil { @@ -274,7 +274,7 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) { return } - if !issue.IsPoster(ctx.User.ID) && !ctx.Repo.IsWriter() { + if !issue.IsPoster(ctx.User.ID) && !ctx.Repo.CanWrite(models.UnitTypeIssues) { ctx.Status(403) return } @@ -288,7 +288,7 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) { // Update the deadline var deadlineUnix util.TimeStamp - if form.Deadline != nil && !form.Deadline.IsZero() && ctx.Repo.IsWriter() { + if form.Deadline != nil && !form.Deadline.IsZero() && ctx.Repo.CanWrite(models.UnitTypeIssues) { deadlineUnix = util.TimeStamp(form.Deadline.Unix()) } @@ -305,8 +305,7 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) { // Pass one or more user logins to replace the set of assignees on this Issue. // Send an empty array ([]) to clear all assignees from the Issue. - if ctx.Repo.IsWriter() && (form.Assignees != nil || form.Assignee != nil) { - + if ctx.Repo.CanWrite(models.UnitTypeIssues) && (form.Assignees != nil || form.Assignee != nil) { oneAssignee := "" if form.Assignee != nil { oneAssignee = *form.Assignee @@ -319,7 +318,7 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) { } } - if ctx.Repo.IsWriter() && form.Milestone != nil && + if ctx.Repo.CanWrite(models.UnitTypeIssues) && form.Milestone != nil && issue.MilestoneID != *form.Milestone { oldMilestoneID := issue.MilestoneID issue.MilestoneID = *form.Milestone @@ -403,7 +402,7 @@ func UpdateIssueDeadline(ctx *context.APIContext, form api.EditDeadlineOption) { return } - if !ctx.Repo.IsWriter() { + if !ctx.Repo.CanWrite(models.UnitTypeIssues) { ctx.Status(403) return } diff --git a/routers/api/v1/repo/issue_label.go b/routers/api/v1/repo/issue_label.go index 35defa25b5..715dd0ed77 100644 --- a/routers/api/v1/repo/issue_label.go +++ b/routers/api/v1/repo/issue_label.go @@ -1,4 +1,5 @@ // Copyright 2016 The Gogs Authors. All rights reserved. +// 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. @@ -90,11 +91,6 @@ func AddIssueLabels(ctx *context.APIContext, form api.IssueLabelsOption) { // responses: // "200": // "$ref": "#/responses/LabelList" - if !ctx.Repo.IsWriter() { - ctx.Status(403) - return - } - issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) if err != nil { if models.IsErrIssueNotExist(err) { @@ -105,6 +101,11 @@ func AddIssueLabels(ctx *context.APIContext, form api.IssueLabelsOption) { return } + if !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) { + ctx.Status(403) + return + } + labels, err := models.GetLabelsInRepoByIDs(ctx.Repo.Repository.ID, form.Labels) if err != nil { ctx.Error(500, "GetLabelsInRepoByIDs", err) @@ -162,11 +163,6 @@ func DeleteIssueLabel(ctx *context.APIContext) { // responses: // "204": // "$ref": "#/responses/empty" - if !ctx.Repo.IsWriter() { - ctx.Status(403) - return - } - issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) if err != nil { if models.IsErrIssueNotExist(err) { @@ -177,6 +173,11 @@ func DeleteIssueLabel(ctx *context.APIContext) { return } + if !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) { + ctx.Status(403) + return + } + label, err := models.GetLabelInRepoByID(ctx.Repo.Repository.ID, ctx.ParamsInt64(":id")) if err != nil { if models.IsErrLabelNotExist(err) { @@ -228,11 +229,6 @@ func ReplaceIssueLabels(ctx *context.APIContext, form api.IssueLabelsOption) { // responses: // "200": // "$ref": "#/responses/LabelList" - if !ctx.Repo.IsWriter() { - ctx.Status(403) - return - } - issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) if err != nil { if models.IsErrIssueNotExist(err) { @@ -243,6 +239,11 @@ func ReplaceIssueLabels(ctx *context.APIContext, form api.IssueLabelsOption) { return } + if !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) { + ctx.Status(403) + return + } + labels, err := models.GetLabelsInRepoByIDs(ctx.Repo.Repository.ID, form.Labels) if err != nil { ctx.Error(500, "GetLabelsInRepoByIDs", err) @@ -294,11 +295,6 @@ func ClearIssueLabels(ctx *context.APIContext) { // responses: // "204": // "$ref": "#/responses/empty" - if !ctx.Repo.IsWriter() { - ctx.Status(403) - return - } - issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) if err != nil { if models.IsErrIssueNotExist(err) { @@ -309,6 +305,11 @@ func ClearIssueLabels(ctx *context.APIContext) { return } + if !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) { + ctx.Status(403) + return + } + if err := issue.ClearLabels(ctx.User); err != nil { ctx.Error(500, "ClearLabels", err) return diff --git a/routers/api/v1/repo/label.go b/routers/api/v1/repo/label.go index 359ccf0e41..df7a370041 100644 --- a/routers/api/v1/repo/label.go +++ b/routers/api/v1/repo/label.go @@ -1,4 +1,5 @@ // Copyright 2016 The Gogs Authors. All rights reserved. +// 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. @@ -123,11 +124,6 @@ func CreateLabel(ctx *context.APIContext, form api.CreateLabelOption) { // responses: // "201": // "$ref": "#/responses/Label" - if !ctx.Repo.IsWriter() { - ctx.Status(403) - return - } - label := &models.Label{ Name: form.Name, Color: form.Color, @@ -173,11 +169,6 @@ func EditLabel(ctx *context.APIContext, form api.EditLabelOption) { // responses: // "200": // "$ref": "#/responses/Label" - if !ctx.Repo.IsWriter() { - ctx.Status(403) - return - } - label, err := models.GetLabelInRepoByID(ctx.Repo.Repository.ID, ctx.ParamsInt64(":id")) if err != nil { if models.IsErrLabelNotExist(err) { @@ -226,11 +217,6 @@ func DeleteLabel(ctx *context.APIContext) { // responses: // "204": // "$ref": "#/responses/empty" - if !ctx.Repo.IsWriter() { - ctx.Status(403) - return - } - if err := models.DeleteLabel(ctx.Repo.Repository.ID, ctx.ParamsInt64(":id")); err != nil { ctx.Error(500, "DeleteLabel", err) return diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index 83ef886d7d..a0117794ad 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -351,7 +351,7 @@ func EditPullRequest(ctx *context.APIContext, form api.EditPullRequestOption) { pr.LoadIssue() issue := pr.Issue - if !issue.IsPoster(ctx.User.ID) && !ctx.Repo.IsWriter() { + if !issue.IsPoster(ctx.User.ID) && !ctx.Repo.CanWrite(models.UnitTypePullRequests) { ctx.Status(403) return } @@ -382,7 +382,7 @@ func EditPullRequest(ctx *context.APIContext, form api.EditPullRequestOption) { // Pass one or more user logins to replace the set of assignees on this Issue. // Send an empty array ([]) to clear all assignees from the Issue. - if ctx.Repo.IsWriter() && (form.Assignees != nil || len(form.Assignee) > 0) { + if ctx.Repo.CanWrite(models.UnitTypePullRequests) && (form.Assignees != nil || len(form.Assignee) > 0) { err = models.UpdateAPIAssignee(issue, form.Assignee, form.Assignees, ctx.User) if err != nil { @@ -395,7 +395,7 @@ func EditPullRequest(ctx *context.APIContext, form api.EditPullRequestOption) { } } - if ctx.Repo.IsWriter() && form.Milestone != 0 && + if ctx.Repo.CanWrite(models.UnitTypePullRequests) && form.Milestone != 0 && issue.MilestoneID != form.Milestone { oldMilestoneID := issue.MilestoneID issue.MilestoneID = form.Milestone @@ -405,7 +405,7 @@ func EditPullRequest(ctx *context.APIContext, form api.EditPullRequestOption) { } } - if ctx.Repo.IsWriter() && (form.Labels != nil && len(form.Labels) > 0) { + if ctx.Repo.CanWrite(models.UnitTypePullRequests) && (form.Labels != nil && len(form.Labels) > 0) { labels, err := models.GetLabelsInRepoByIDs(ctx.Repo.Repository.ID, form.Labels) if err != nil { ctx.Error(500, "GetLabelsInRepoByIDsError", err) @@ -663,7 +663,12 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) } } - if !ctx.User.IsWriterOfRepo(headRepo) && !ctx.User.IsAdmin { + perm, err := models.GetUserRepoPermission(headRepo, ctx.User) + if err != nil { + ctx.ServerError("GetUserRepoPermission", err) + return nil, nil, nil, nil, "", "" + } + if !perm.CanWrite(models.UnitTypeCode) { log.Trace("ParseCompareInfo[%d]: does not have write access or site admin", baseRepo.ID) ctx.Status(404) return nil, nil, nil, nil, "", "" diff --git a/routers/api/v1/repo/release.go b/routers/api/v1/repo/release.go index 282ff582c5..caa2905e93 100644 --- a/routers/api/v1/repo/release.go +++ b/routers/api/v1/repo/release.go @@ -122,10 +122,6 @@ func CreateRelease(ctx *context.APIContext, form api.CreateReleaseOption) { // responses: // "201": // "$ref": "#/responses/Release" - if ctx.Repo.AccessMode < models.AccessModeWrite { - ctx.Status(403) - return - } rel, err := models.GetRelease(ctx.Repo.Repository.ID, form.TagName) if err != nil { if !models.IsErrReleaseNotExist(err) { @@ -209,10 +205,6 @@ func EditRelease(ctx *context.APIContext, form api.EditReleaseOption) { // responses: // "200": // "$ref": "#/responses/Release" - if ctx.Repo.AccessMode < models.AccessModeWrite { - ctx.Status(403) - return - } id := ctx.ParamsInt64(":id") rel, err := models.GetReleaseByID(id) if err != nil && !models.IsErrReleaseNotExist(err) { @@ -285,10 +277,6 @@ func DeleteRelease(ctx *context.APIContext) { // responses: // "204": // "$ref": "#/responses/empty" - if ctx.Repo.AccessMode < models.AccessModeWrite { - ctx.Status(403) - return - } id := ctx.ParamsInt64(":id") rel, err := models.GetReleaseByID(id) if err != nil && !models.IsErrReleaseNotExist(err) { diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index bf6346eebd..6d8125a77f 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -1,4 +1,5 @@ // Copyright 2014 The Gogs Authors. All rights reserved. +// 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. @@ -183,11 +184,6 @@ func Search(ctx *context.APIContext) { return } - var userID int64 - if ctx.IsSigned { - userID = ctx.User.ID - } - results := make([]*api.Repository, len(repos)) for i, repo := range repos { if err = repo.GetOwner(); err != nil { @@ -197,7 +193,7 @@ func Search(ctx *context.APIContext) { }) return } - accessMode, err := models.AccessLevel(userID, repo) + accessMode, err := models.AccessLevel(ctx.User, repo) if err != nil { ctx.JSON(500, api.SearchError{ OK: false, @@ -469,15 +465,15 @@ func GetByID(ctx *context.APIContext) { return } - access, err := models.AccessLevel(ctx.User.ID, repo) + perm, err := models.GetUserRepoPermission(repo, ctx.User) if err != nil { ctx.Error(500, "AccessLevel", err) return - } else if access < models.AccessModeRead { + } else if !perm.HasAccess() { ctx.Status(404) return } - ctx.JSON(200, repo.APIFormat(access)) + ctx.JSON(200, repo.APIFormat(perm.AccessMode)) } // Delete one repository @@ -503,10 +499,6 @@ func Delete(ctx *context.APIContext) { // "$ref": "#/responses/empty" // "403": // "$ref": "#/responses/forbidden" - if !ctx.Repo.IsAdmin() { - ctx.Error(403, "", "Must have admin rights") - return - } owner := ctx.Repo.Owner repo := ctx.Repo.Repository @@ -553,7 +545,7 @@ func MirrorSync(ctx *context.APIContext) { // "$ref": "#/responses/empty" repo := ctx.Repo.Repository - if !ctx.Repo.IsWriter() { + if !ctx.Repo.CanWrite(models.UnitTypeCode) { ctx.Error(403, "MirrorSync", "Must have write access") } diff --git a/routers/api/v1/user/repo.go b/routers/api/v1/user/repo.go index 5dccfac960..1ddb3bd57b 100644 --- a/routers/api/v1/user/repo.go +++ b/routers/api/v1/user/repo.go @@ -17,13 +17,10 @@ func listUserRepos(ctx *context.APIContext, u *models.User, private bool) { ctx.Error(500, "GetUserRepositories", err) return } + apiRepos := make([]*api.Repository, 0, len(repos)) - var ctxUserID int64 - if ctx.User != nil { - ctxUserID = ctx.User.ID - } for i := range repos { - access, err := models.AccessLevel(ctxUserID, repos[i]) + access, err := models.AccessLevel(ctx.User, repos[i]) if err != nil { ctx.Error(500, "AccessLevel", err) return diff --git a/routers/api/v1/user/star.go b/routers/api/v1/user/star.go index 1cf4f5239c..b0016399c8 100644 --- a/routers/api/v1/user/star.go +++ b/routers/api/v1/user/star.go @@ -13,15 +13,15 @@ import ( // getStarredRepos returns the repos that the user with the specified userID has // starred -func getStarredRepos(userID int64, private bool) ([]*api.Repository, error) { - starredRepos, err := models.GetStarredRepos(userID, private) +func getStarredRepos(user *models.User, private bool) ([]*api.Repository, error) { + starredRepos, err := models.GetStarredRepos(user.ID, private) if err != nil { return nil, err } repos := make([]*api.Repository, len(starredRepos)) for i, starred := range starredRepos { - access, err := models.AccessLevel(userID, starred) + access, err := models.AccessLevel(user, starred) if err != nil { return nil, err } @@ -48,7 +48,7 @@ func GetStarredRepos(ctx *context.APIContext) { // "$ref": "#/responses/RepositoryList" user := GetUserByParams(ctx) private := user.ID == ctx.User.ID - repos, err := getStarredRepos(user.ID, private) + repos, err := getStarredRepos(user, private) if err != nil { ctx.Error(500, "getStarredRepos", err) } @@ -65,7 +65,7 @@ func GetMyStarredRepos(ctx *context.APIContext) { // responses: // "200": // "$ref": "#/responses/RepositoryList" - repos, err := getStarredRepos(ctx.User.ID, true) + repos, err := getStarredRepos(ctx.User, true) if err != nil { ctx.Error(500, "getStarredRepos", err) } diff --git a/routers/api/v1/user/watch.go b/routers/api/v1/user/watch.go index 2971bf6869..4afa18be2a 100644 --- a/routers/api/v1/user/watch.go +++ b/routers/api/v1/user/watch.go @@ -14,15 +14,15 @@ import ( // getWatchedRepos returns the repos that the user with the specified userID is // watching -func getWatchedRepos(userID int64, private bool) ([]*api.Repository, error) { - watchedRepos, err := models.GetWatchedRepos(userID, private) +func getWatchedRepos(user *models.User, private bool) ([]*api.Repository, error) { + watchedRepos, err := models.GetWatchedRepos(user.ID, private) if err != nil { return nil, err } repos := make([]*api.Repository, len(watchedRepos)) for i, watched := range watchedRepos { - access, err := models.AccessLevel(userID, watched) + access, err := models.AccessLevel(user, watched) if err != nil { return nil, err } @@ -49,7 +49,7 @@ func GetWatchedRepos(ctx *context.APIContext) { // "$ref": "#/responses/RepositoryList" user := GetUserByParams(ctx) private := user.ID == ctx.User.ID - repos, err := getWatchedRepos(user.ID, private) + repos, err := getWatchedRepos(user, private) if err != nil { ctx.Error(500, "getWatchedRepos", err) } @@ -66,7 +66,7 @@ func GetMyWatchedRepos(ctx *context.APIContext) { // responses: // "200": // "$ref": "#/responses/RepositoryList" - repos, err := getWatchedRepos(ctx.User.ID, true) + repos, err := getWatchedRepos(ctx.User, true) if err != nil { ctx.Error(500, "getWatchedRepos", err) } diff --git a/routers/private/internal.go b/routers/private/internal.go index 23e0122642..0221b1fee8 100644 --- a/routers/private/internal.go +++ b/routers/private/internal.go @@ -38,8 +38,8 @@ func GetRepositoryByOwnerAndName(ctx *macaron.Context) { ctx.JSON(200, repo) } -//AccessLevel chainload to models.AccessLevel -func AccessLevel(ctx *macaron.Context) { +//CheckUnitUser chainload to models.CheckUnitUser +func CheckUnitUser(ctx *macaron.Context) { repoID := ctx.ParamsInt64(":repoid") userID := ctx.ParamsInt64(":userid") repo, err := models.GetRepositoryByID(repoID) @@ -49,32 +49,27 @@ func AccessLevel(ctx *macaron.Context) { }) return } - al, err := models.AccessLevel(userID, repo) - if err != nil { - ctx.JSON(500, map[string]interface{}{ - "err": err.Error(), - }) - return + + var user *models.User + if userID > 0 { + user, err = models.GetUserByID(userID) + if err != nil { + ctx.JSON(500, map[string]interface{}{ + "err": err.Error(), + }) + return + } } - ctx.JSON(200, al) -} -//CheckUnitUser chainload to models.CheckUnitUser -func CheckUnitUser(ctx *macaron.Context) { - repoID := ctx.ParamsInt64(":repoid") - userID := ctx.ParamsInt64(":userid") - repo, err := models.GetRepositoryByID(repoID) + perm, err := models.GetUserRepoPermission(repo, user) if err != nil { ctx.JSON(500, map[string]interface{}{ "err": err.Error(), }) return } - if repo.CheckUnitUser(userID, ctx.QueryBool("isAdmin"), models.UnitType(ctx.QueryInt("unitType"))) { - ctx.PlainText(200, []byte("success")) - return - } - ctx.PlainText(404, []byte("no access")) + + ctx.JSON(200, perm.UnitAccessMode(models.UnitType(ctx.QueryInt("unitType")))) } // RegisterRoutes registers all internal APIs routes to web application. @@ -85,7 +80,6 @@ func RegisterRoutes(m *macaron.Macaron) { m.Get("/ssh/:id/user", GetUserByKeyID) m.Post("/ssh/:id/update", UpdatePublicKey) m.Post("/repositories/:repoid/keys/:keyid/update", UpdateDeployKey) - m.Get("/repositories/:repoid/user/:userid/accesslevel", AccessLevel) m.Get("/repositories/:repoid/user/:userid/checkunituser", CheckUnitUser) m.Get("/repositories/:repoid/has-keys/:keyid", HasDeployKey) m.Post("/push/update", PushUpdate) diff --git a/routers/repo/activity.go b/routers/repo/activity.go index a0a25dc936..5d90d73506 100644 --- a/routers/repo/activity.go +++ b/routers/repo/activity.go @@ -45,9 +45,9 @@ func Activity(ctx *context.Context) { var err error if ctx.Data["Activity"], err = models.GetActivityStats(ctx.Repo.Repository.ID, timeFrom, - ctx.Repo.Repository.UnitEnabled(models.UnitTypeReleases), - ctx.Repo.Repository.UnitEnabled(models.UnitTypeIssues), - ctx.Repo.Repository.UnitEnabled(models.UnitTypePullRequests)); err != nil { + ctx.Repo.CanRead(models.UnitTypeReleases), + ctx.Repo.CanRead(models.UnitTypeIssues), + ctx.Repo.CanRead(models.UnitTypePullRequests)); err != nil { ctx.ServerError("GetActivityStats", err) return } diff --git a/routers/repo/branch.go b/routers/repo/branch.go index 7b1e2a8bbe..295aeaa24b 100644 --- a/routers/repo/branch.go +++ b/routers/repo/branch.go @@ -1,4 +1,5 @@ // Copyright 2014 The Gogs Authors. All rights reserved. +// 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. @@ -33,7 +34,7 @@ func Branches(ctx *context.Context) { ctx.Data["Title"] = "Branches" ctx.Data["IsRepoToolbarBranches"] = true ctx.Data["DefaultBranch"] = ctx.Repo.Repository.DefaultBranch - ctx.Data["IsWriter"] = ctx.Repo.IsWriter() + ctx.Data["IsWriter"] = ctx.Repo.CanWrite(models.UnitTypeCode) ctx.Data["IsMirror"] = ctx.Repo.Repository.IsMirror ctx.Data["PageIsViewCode"] = true ctx.Data["PageIsBranches"] = true @@ -161,7 +162,7 @@ func loadBranches(ctx *context.Context) []*Branch { } } - if ctx.Repo.IsWriter() { + if ctx.Repo.CanWrite(models.UnitTypeCode) { deletedBranches, err := getDeletedBranches(ctx) if err != nil { ctx.ServerError("getDeletedBranches", err) diff --git a/routers/repo/http.go b/routers/repo/http.go index 5b469754ad..ec5fbe6c0d 100644 --- a/routers/repo/http.go +++ b/routers/repo/http.go @@ -182,36 +182,19 @@ func HTTP(ctx *context.Context) { } } - if !isPublicPull { - has, err := models.HasAccess(authUser.ID, repo, accessMode) - if err != nil { - ctx.ServerError("HasAccess", err) - return - } else if !has { - if accessMode == models.AccessModeRead { - has, err = models.HasAccess(authUser.ID, repo, models.AccessModeWrite) - if err != nil { - ctx.ServerError("HasAccess2", err) - return - } else if !has { - ctx.HandleText(http.StatusForbidden, "User permission denied") - return - } - } else { - ctx.HandleText(http.StatusForbidden, "User permission denied") - return - } - } + perm, err := models.GetUserRepoPermission(repo, authUser) + if err != nil { + ctx.ServerError("GetUserRepoPermission", err) + return + } - if !isPull && repo.IsMirror { - ctx.HandleText(http.StatusForbidden, "mirror repository is read-only") - return - } + if !perm.CanAccess(accessMode, unitType) { + ctx.HandleText(http.StatusForbidden, "User permission denied") + return } - if !repo.CheckUnitUser(authUser.ID, authUser.IsAdmin, unitType) { - ctx.HandleText(http.StatusForbidden, fmt.Sprintf("User %s does not have allowed access to repository %s 's code", - authUser.Name, repo.RepoPath())) + if !isPull && repo.IsMirror { + ctx.HandleText(http.StatusForbidden, "mirror repository is read-only") return } diff --git a/routers/repo/issue.go b/routers/repo/issue.go index 08bdf311f9..8d95f33940 100644 --- a/routers/repo/issue.go +++ b/routers/repo/issue.go @@ -1,4 +1,5 @@ // Copyright 2014 The Gogs Authors. All rights reserved. +// 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. @@ -63,8 +64,8 @@ var ( // MustEnableIssues check if repository enable internal issues func MustEnableIssues(ctx *context.Context) { - if !ctx.Repo.Repository.UnitEnabled(models.UnitTypeIssues) && - !ctx.Repo.Repository.UnitEnabled(models.UnitTypeExternalTracker) { + if !ctx.Repo.CanRead(models.UnitTypeIssues) && + !ctx.Repo.CanRead(models.UnitTypeExternalTracker) { ctx.NotFound("MustEnableIssues", nil) return } @@ -76,9 +77,9 @@ func MustEnableIssues(ctx *context.Context) { } } -// MustAllowPulls check if repository enable pull requests +// MustAllowPulls check if repository enable pull requests and user have right to do that func MustAllowPulls(ctx *context.Context) { - if !ctx.Repo.Repository.AllowsPulls() { + if !ctx.Repo.Repository.CanEnablePulls() || !ctx.Repo.CanRead(models.UnitTypePullRequests) { ctx.NotFound("MustAllowPulls", nil) return } @@ -280,7 +281,7 @@ func RetrieveRepoMilestonesAndAssignees(ctx *context.Context, repo *models.Repos // RetrieveRepoMetas find all the meta information of a repository func RetrieveRepoMetas(ctx *context.Context, repo *models.Repository) []*models.Label { - if !ctx.Repo.IsWriter() { + if !ctx.Repo.CanWrite(models.UnitTypeIssues) { return nil } @@ -369,7 +370,7 @@ func NewIssue(ctx *context.Context) { } // ValidateRepoMetas check and returns repository's meta informations -func ValidateRepoMetas(ctx *context.Context, form auth.CreateIssueForm) ([]int64, []int64, int64) { +func ValidateRepoMetas(ctx *context.Context, form auth.CreateIssueForm, isPull bool) ([]int64, []int64, int64) { var ( repo = ctx.Repo.Repository err error @@ -380,10 +381,6 @@ func ValidateRepoMetas(ctx *context.Context, form auth.CreateIssueForm) ([]int64 return nil, nil, 0 } - if !ctx.Repo.IsWriter() { - return nil, nil, 0 - } - var labelIDs []int64 hasSelected := false // Check labels. @@ -427,9 +424,19 @@ func ValidateRepoMetas(ctx *context.Context, form auth.CreateIssueForm) ([]int64 // Check if the passed assignees actually exists and has write access to the repo for _, aID := range assigneeIDs { - _, err = repo.GetUserIfHasWriteAccess(aID) + user, err := models.GetUserByID(aID) + if err != nil { + ctx.ServerError("GetUserByID", err) + return nil, nil, 0 + } + + perm, err := models.GetUserRepoPermission(repo, user) if err != nil { - ctx.ServerError("GetUserIfHasWriteAccess", err) + ctx.ServerError("GetUserRepoPermission", err) + return nil, nil, 0 + } + if !perm.CanWriteIssuesOrPulls(isPull) { + ctx.ServerError("CanWriteIssuesOrPulls", fmt.Errorf("No permission for %s", user.Name)) return nil, nil, 0 } } @@ -458,7 +465,7 @@ func NewIssuePost(ctx *context.Context, form auth.CreateIssueForm) { attachments []string ) - labelIDs, assigneeIDs, milestoneID := ValidateRepoMetas(ctx, form) + labelIDs, assigneeIDs, milestoneID := ValidateRepoMetas(ctx, form, false) if ctx.Written() { return } @@ -498,31 +505,23 @@ func NewIssuePost(ctx *context.Context, form auth.CreateIssueForm) { // commentTag returns the CommentTag for a comment in/with the given repo, poster and issue func commentTag(repo *models.Repository, poster *models.User, issue *models.Issue) (models.CommentTag, error) { - if repo.IsOwnedBy(poster.ID) { - return models.CommentTagOwner, nil - } else if repo.Owner.IsOrganization() { - isOwner, err := repo.Owner.IsOwnedBy(poster.ID) - if err != nil { - return models.CommentTagNone, err - } else if isOwner { - return models.CommentTagOwner, nil - } + perm, err := models.GetUserRepoPermission(repo, poster) + if err != nil { + return models.CommentTagNone, err } - if poster.IsWriterOfRepo(repo) { - return models.CommentTagWriter, nil + if perm.IsOwner() { + return models.CommentTagOwner, nil } else if poster.ID == issue.PosterID { return models.CommentTagPoster, nil + } else if perm.CanWrite(models.UnitTypeCode) { + return models.CommentTagWriter, nil } + return models.CommentTagNone, nil } // ViewIssue render issue view page func ViewIssue(ctx *context.Context) { - ctx.Data["RequireHighlightJS"] = true - ctx.Data["RequireDropzone"] = true - ctx.Data["RequireTribute"] = true - renderAttachmentSettings(ctx) - issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) if err != nil { if models.IsErrIssueNotExist(err) { @@ -532,25 +531,6 @@ func ViewIssue(ctx *context.Context) { } return } - ctx.Data["Title"] = fmt.Sprintf("#%d - %s", issue.Index, issue.Title) - - var iw *models.IssueWatch - var exists bool - if ctx.User != nil { - iw, exists, err = models.GetIssueWatch(ctx.User.ID, issue.ID) - if err != nil { - ctx.ServerError("GetIssueWatch", err) - return - } - if !exists { - iw = &models.IssueWatch{ - UserID: ctx.User.ID, - IssueID: issue.ID, - IsWatching: models.IsWatching(ctx.User.ID, ctx.Repo.Repository.ID), - } - } - } - ctx.Data["IssueWatch"] = iw // Make sure type and URL matches. if ctx.Params(":type") == "issues" && issue.IsPull { @@ -576,6 +556,31 @@ func ViewIssue(ctx *context.Context) { ctx.Data["PageIsIssueList"] = true } + ctx.Data["RequireHighlightJS"] = true + ctx.Data["RequireDropzone"] = true + ctx.Data["RequireTribute"] = true + renderAttachmentSettings(ctx) + + ctx.Data["Title"] = fmt.Sprintf("#%d - %s", issue.Index, issue.Title) + + var iw *models.IssueWatch + var exists bool + if ctx.User != nil { + iw, exists, err = models.GetIssueWatch(ctx.User.ID, issue.ID) + if err != nil { + ctx.ServerError("GetIssueWatch", err) + return + } + if !exists { + iw = &models.IssueWatch{ + UserID: ctx.User.ID, + IssueID: issue.ID, + IsWatching: models.IsWatching(ctx.User.ID, ctx.Repo.Repository.ID), + } + } + } + ctx.Data["IssueWatch"] = iw + issue.RenderedContent = string(markdown.Render([]byte(issue.Content), ctx.Repo.RepoLink, ctx.Repo.Repository.ComposeMetas())) @@ -616,7 +621,7 @@ func ViewIssue(ctx *context.Context) { ctx.Data["Labels"] = labels // Check milestone and assignee. - if ctx.Repo.IsWriter() { + if ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) { RetrieveRepoMilestonesAndAssignees(ctx, repo) if ctx.Written() { return @@ -761,13 +766,20 @@ func ViewIssue(ctx *context.Context) { if ctx.IsSigned { if err := pull.GetHeadRepo(); err != nil { log.Error(4, "GetHeadRepo: %v", err) - } else if pull.HeadRepo != nil && pull.HeadBranch != pull.HeadRepo.DefaultBranch && ctx.User.IsWriterOfRepo(pull.HeadRepo) { - // Check if branch is not protected - if protected, err := pull.HeadRepo.IsProtectedBranch(pull.HeadBranch, ctx.User); err != nil { - log.Error(4, "IsProtectedBranch: %v", err) - } else if !protected { - canDelete = true - ctx.Data["DeleteBranchLink"] = ctx.Repo.RepoLink + "/pulls/" + com.ToStr(issue.Index) + "/cleanup" + } else if pull.HeadRepo != nil && pull.HeadBranch != pull.HeadRepo.DefaultBranch { + perm, err := models.GetUserRepoPermission(pull.HeadRepo, ctx.User) + if err != nil { + ctx.ServerError("GetUserRepoPermission", err) + return + } + if perm.CanWrite(models.UnitTypeCode) { + // Check if branch is not protected + if protected, err := pull.HeadRepo.IsProtectedBranch(pull.HeadBranch, ctx.User); err != nil { + log.Error(4, "IsProtectedBranch: %v", err) + } else if !protected { + canDelete = true + ctx.Data["DeleteBranchLink"] = ctx.Repo.RepoLink + "/pulls/" + com.ToStr(issue.Index) + "/cleanup" + } } } } @@ -779,7 +791,7 @@ func ViewIssue(ctx *context.Context) { } prConfig := prUnit.PullRequestsConfig() - ctx.Data["AllowMerge"] = ctx.Data["IsRepositoryWriter"] + ctx.Data["AllowMerge"] = ctx.Repo.CanWrite(models.UnitTypeCode) if err := pull.CheckUserAllowedToMerge(ctx.User); err != nil { if !models.IsErrNotAllowedToMerge(err) { ctx.ServerError("CheckUserAllowedToMerge", err) @@ -818,8 +830,9 @@ func ViewIssue(ctx *context.Context) { ctx.Data["NumParticipants"] = len(participants) ctx.Data["Issue"] = issue ctx.Data["ReadOnly"] = true - ctx.Data["IsIssueOwner"] = ctx.Repo.IsWriter() || (ctx.IsSigned && issue.IsPoster(ctx.User.ID)) ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login?redirect_to=" + ctx.Data["Link"].(string) + ctx.Data["IsIssuePoster"] = ctx.IsSigned && issue.IsPoster(ctx.User.ID) + ctx.Data["IsIssueWriter"] = ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) ctx.HTML(200, tplIssueView) } @@ -842,8 +855,8 @@ func GetActionIssue(ctx *context.Context) *models.Issue { } func checkIssueRights(ctx *context.Context, issue *models.Issue) { - if issue.IsPull && !ctx.Repo.Repository.UnitEnabled(models.UnitTypePullRequests) || - !issue.IsPull && !ctx.Repo.Repository.UnitEnabled(models.UnitTypeIssues) { + if issue.IsPull && !ctx.Repo.CanRead(models.UnitTypePullRequests) || + !issue.IsPull && !ctx.Repo.CanRead(models.UnitTypeIssues) { ctx.NotFound("IssueOrPullRequestUnitNotAllowed", nil) } } @@ -868,8 +881,8 @@ func getActionIssues(ctx *context.Context) []*models.Issue { return nil } // Check access rights for all issues - issueUnitEnabled := ctx.Repo.Repository.UnitEnabled(models.UnitTypeIssues) - prUnitEnabled := ctx.Repo.Repository.UnitEnabled(models.UnitTypePullRequests) + issueUnitEnabled := ctx.Repo.CanRead(models.UnitTypeIssues) + prUnitEnabled := ctx.Repo.CanRead(models.UnitTypePullRequests) for _, issue := range issues { if issue.IsPull && !prUnitEnabled || !issue.IsPull && !issueUnitEnabled { ctx.NotFound("IssueOrPullRequestUnitNotAllowed", nil) @@ -890,7 +903,7 @@ func UpdateIssueTitle(ctx *context.Context) { return } - if !ctx.IsSigned || (!issue.IsPoster(ctx.User.ID) && !ctx.Repo.IsWriter()) { + if !ctx.IsSigned || (!issue.IsPoster(ctx.User.ID) && !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull)) { ctx.Error(403) return } @@ -918,7 +931,7 @@ func UpdateIssueContent(ctx *context.Context) { return } - if !ctx.IsSigned || (ctx.User.ID != issue.PosterID && !ctx.Repo.IsWriter()) { + if !ctx.IsSigned || (ctx.User.ID != issue.PosterID && !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull)) { ctx.Error(403) return } @@ -1037,6 +1050,11 @@ func NewComment(ctx *context.Context, form auth.CreateCommentForm) { return } + if !ctx.IsSigned || (ctx.User.ID != issue.PosterID && !ctx.Repo.CanReadIssuesOrPulls(issue.IsPull)) { + ctx.Error(403) + return + } + var attachments []string if setting.AttachmentEnabled { attachments = form.Files @@ -1051,7 +1069,7 @@ func NewComment(ctx *context.Context, form auth.CreateCommentForm) { var comment *models.Comment defer func() { // Check if issue admin/poster changes the status of issue. - if (ctx.Repo.IsWriter() || (ctx.IsSigned && issue.IsPoster(ctx.User.ID))) && + if (ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) || (ctx.IsSigned && issue.IsPoster(ctx.User.ID))) && (form.Status == "reopen" || form.Status == "close") && !(issue.IsPull && issue.PullRequest.HasMerged) { @@ -1140,7 +1158,12 @@ func UpdateCommentContent(ctx *context.Context) { return } - if !ctx.IsSigned || (ctx.User.ID != comment.PosterID && !ctx.Repo.IsAdmin()) { + if err := comment.LoadIssue(); err != nil { + ctx.NotFoundOrServerError("LoadIssue", models.IsErrIssueNotExist, err) + return + } + + if !ctx.IsSigned || (ctx.User.ID != comment.PosterID && !ctx.Repo.CanWriteIssuesOrPulls(comment.Issue.IsPull)) { ctx.Error(403) return } else if comment.Type != models.CommentTypeComment && comment.Type != models.CommentTypeCode { @@ -1174,7 +1197,12 @@ func DeleteComment(ctx *context.Context) { return } - if !ctx.IsSigned || (ctx.User.ID != comment.PosterID && !ctx.Repo.IsAdmin()) { + if err := comment.LoadIssue(); err != nil { + ctx.NotFoundOrServerError("LoadIssue", models.IsErrIssueNotExist, err) + return + } + + if !ctx.IsSigned || (ctx.User.ID != comment.PosterID && !ctx.Repo.CanWriteIssuesOrPulls(comment.Issue.IsPull)) { ctx.Error(403) return } else if comment.Type != models.CommentTypeComment && comment.Type != models.CommentTypeCode { @@ -1417,6 +1445,11 @@ func ChangeIssueReaction(ctx *context.Context, form auth.ReactionForm) { return } + if !ctx.IsSigned || (ctx.User.ID != issue.PosterID && !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull)) { + ctx.Error(403) + return + } + if ctx.HasError() { ctx.ServerError("ChangeIssueReaction", errors.New(ctx.GetErrMsg())) return @@ -1486,20 +1519,22 @@ func ChangeCommentReaction(ctx *context.Context, form auth.ReactionForm) { return } - issue, err := models.GetIssueByID(comment.IssueID) - checkIssueRights(ctx, issue) - if ctx.Written() { + if err := comment.LoadIssue(); err != nil { + ctx.NotFoundOrServerError("LoadIssue", models.IsErrIssueNotExist, err) return } - if ctx.HasError() { - ctx.ServerError("ChangeCommentReaction", errors.New(ctx.GetErrMsg())) + if !ctx.IsSigned || (ctx.User.ID != comment.PosterID && !ctx.Repo.CanWriteIssuesOrPulls(comment.Issue.IsPull)) { + ctx.Error(403) + return + } else if comment.Type != models.CommentTypeComment && comment.Type != models.CommentTypeCode { + ctx.Error(204) return } switch ctx.Params(":action") { case "react": - reaction, err := models.CreateCommentReaction(ctx.User, issue, comment, form.Content) + reaction, err := models.CreateCommentReaction(ctx.User, comment.Issue, comment, form.Content) if err != nil { log.Info("CreateCommentReaction: %s", err) break @@ -1511,9 +1546,9 @@ func ChangeCommentReaction(ctx *context.Context, form auth.ReactionForm) { break } - log.Trace("Reaction for comment created: %d/%d/%d/%d", ctx.Repo.Repository.ID, issue.ID, comment.ID, reaction.ID) + log.Trace("Reaction for comment created: %d/%d/%d/%d", ctx.Repo.Repository.ID, comment.Issue.ID, comment.ID, reaction.ID) case "unreact": - if err := models.DeleteCommentReaction(ctx.User, issue, comment, form.Content); err != nil { + if err := models.DeleteCommentReaction(ctx.User, comment.Issue, comment, form.Content); err != nil { ctx.ServerError("DeleteCommentReaction", err) return } @@ -1525,7 +1560,7 @@ func ChangeCommentReaction(ctx *context.Context, form auth.ReactionForm) { break } - log.Trace("Reaction for comment removed: %d/%d/%d", ctx.Repo.Repository.ID, issue.ID, comment.ID) + log.Trace("Reaction for comment removed: %d/%d/%d", ctx.Repo.Repository.ID, comment.Issue.ID, comment.ID) default: ctx.NotFound(fmt.Sprintf("Unknown action %s", ctx.Params(":action")), nil) return diff --git a/routers/repo/issue_watch.go b/routers/repo/issue_watch.go index a499b70d9c..c6a436801a 100644 --- a/routers/repo/issue_watch.go +++ b/routers/repo/issue_watch.go @@ -14,23 +14,28 @@ import ( ) // IssueWatch sets issue watching -func IssueWatch(c *context.Context) { - watch, err := strconv.ParseBool(c.Req.PostForm.Get("watch")) - if err != nil { - c.ServerError("watch is not bool", err) +func IssueWatch(ctx *context.Context) { + issue := GetActionIssue(ctx) + if ctx.Written() { return } - issue := GetActionIssue(c) - if c.Written() { + if !ctx.IsSigned || (ctx.User.ID != issue.PosterID && !ctx.Repo.CanReadIssuesOrPulls(issue.IsPull)) { + ctx.Error(403) + return + } + + watch, err := strconv.ParseBool(ctx.Req.PostForm.Get("watch")) + if err != nil { + ctx.ServerError("watch is not bool", err) return } - if err := models.CreateOrUpdateIssueWatch(c.User.ID, issue.ID, watch); err != nil { - c.ServerError("CreateOrUpdateIssueWatch", err) + if err := models.CreateOrUpdateIssueWatch(ctx.User.ID, issue.ID, watch); err != nil { + ctx.ServerError("CreateOrUpdateIssueWatch", err) return } - url := fmt.Sprintf("%s/issues/%d", c.Repo.RepoLink, issue.Index) - c.Redirect(url, http.StatusSeeOther) + url := fmt.Sprintf("%s/issues/%d", ctx.Repo.RepoLink, issue.Index) + ctx.Redirect(url, http.StatusSeeOther) } diff --git a/routers/repo/pull.go b/routers/repo/pull.go index 4ec1c27cea..4adfb96e74 100644 --- a/routers/repo/pull.go +++ b/routers/repo/pull.go @@ -57,7 +57,13 @@ func getForkRepository(ctx *context.Context) *models.Repository { return nil } - if !forkRepo.CanBeForked() || !forkRepo.HasAccess(ctx.User) { + perm, err := models.GetUserRepoPermission(forkRepo, ctx.User) + if err != nil { + ctx.ServerError("GetUserRepoPermission", err) + return nil + } + + if forkRepo.IsBare || !perm.CanRead(models.UnitTypeCode) { ctx.NotFound("getForkRepository", nil) return nil } @@ -669,7 +675,12 @@ func ParseCompareInfo(ctx *context.Context) (*models.User, *models.Repository, * } } - if !ctx.User.IsWriterOfRepo(headRepo) && !ctx.User.IsAdmin { + perm, err := models.GetUserRepoPermission(headRepo, ctx.User) + if err != nil { + ctx.ServerError("GetUserRepoPermission", err) + return nil, nil, nil, nil, "", "" + } + if !perm.CanWrite(models.UnitTypeCode) { log.Trace("ParseCompareInfo[%d]: does not have write access or site admin", baseRepo.ID) ctx.NotFound("ParseCompareInfo", nil) return nil, nil, nil, nil, "", "" @@ -823,7 +834,7 @@ func CompareAndPullRequestPost(ctx *context.Context, form auth.CreateIssueForm) return } - labelIDs, assigneeIDs, milestoneID := ValidateRepoMetas(ctx, form) + labelIDs, assigneeIDs, milestoneID := ValidateRepoMetas(ctx, form, true) if ctx.Written() { return } @@ -969,7 +980,12 @@ func CleanUpPullRequest(ctx *context.Context) { return } - if !ctx.User.IsWriterOfRepo(pr.HeadRepo) { + perm, err := models.GetUserRepoPermission(pr.HeadRepo, ctx.User) + if err != nil { + ctx.ServerError("GetUserRepoPermission", err) + return + } + if !perm.CanWrite(models.UnitTypeCode) { ctx.NotFound("CleanUpPullRequest", nil) return } diff --git a/routers/repo/release.go b/routers/repo/release.go index bae87efdcd..5a869520f0 100644 --- a/routers/repo/release.go +++ b/routers/repo/release.go @@ -1,4 +1,5 @@ // Copyright 2014 The Gogs Authors. All rights reserved. +// 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. @@ -65,8 +66,11 @@ func Releases(ctx *context.Context) { limit = 10 } + writeAccess := ctx.Repo.CanWrite(models.UnitTypeReleases) + ctx.Data["CanCreateRelease"] = writeAccess + opts := models.FindReleasesOptions{ - IncludeDrafts: ctx.Repo.IsWriter(), + IncludeDrafts: writeAccess, IncludeTags: true, } diff --git a/routers/repo/setting_protected_branch.go b/routers/repo/setting_protected_branch.go index db12f0afcd..c8f6f843da 100644 --- a/routers/repo/setting_protected_branch.go +++ b/routers/repo/setting_protected_branch.go @@ -165,12 +165,21 @@ func SettingsProtectedBranchPost(ctx *context.Context, f auth.ProtectBranchForm) } } + var whitelistUsers, whitelistTeams, mergeWhitelistUsers, mergeWhitelistTeams []int64 protectBranch.EnableWhitelist = f.EnableWhitelist - whitelistUsers, _ := base.StringsToInt64s(strings.Split(f.WhitelistUsers, ",")) - whitelistTeams, _ := base.StringsToInt64s(strings.Split(f.WhitelistTeams, ",")) + if strings.TrimSpace(f.WhitelistUsers) != "" { + whitelistUsers, _ = base.StringsToInt64s(strings.Split(f.WhitelistUsers, ",")) + } + if strings.TrimSpace(f.WhitelistTeams) != "" { + whitelistTeams, _ = base.StringsToInt64s(strings.Split(f.WhitelistTeams, ",")) + } protectBranch.EnableMergeWhitelist = f.EnableMergeWhitelist - mergeWhitelistUsers, _ := base.StringsToInt64s(strings.Split(f.MergeWhitelistUsers, ",")) - mergeWhitelistTeams, _ := base.StringsToInt64s(strings.Split(f.MergeWhitelistTeams, ",")) + if strings.TrimSpace(f.MergeWhitelistUsers) != "" { + mergeWhitelistUsers, _ = base.StringsToInt64s(strings.Split(f.MergeWhitelistUsers, ",")) + } + if strings.TrimSpace(f.MergeWhitelistTeams) != "" { + mergeWhitelistTeams, _ = base.StringsToInt64s(strings.Split(f.MergeWhitelistTeams, ",")) + } err = models.UpdateProtectBranch(ctx.Repo.Repository, protectBranch, whitelistUsers, whitelistTeams, mergeWhitelistUsers, mergeWhitelistTeams) if err != nil { ctx.ServerError("UpdateProtectBranch", err) diff --git a/routers/repo/view.go b/routers/repo/view.go index 657fe315a2..78a305aa28 100644 --- a/routers/repo/view.go +++ b/routers/repo/view.go @@ -137,7 +137,7 @@ func renderDirectory(ctx *context.Context, treeLink string) { ctx.Data["LatestCommitStatus"] = models.CalcCommitStatus(statuses) // Check permission to add or upload new file. - if ctx.Repo.IsWriter() && ctx.Repo.IsViewBranch { + if ctx.Repo.CanWrite(models.UnitTypeCode) && ctx.Repo.IsViewBranch { ctx.Data["CanAddFile"] = true ctx.Data["CanUploadFile"] = setting.Repository.Upload.Enabled } @@ -256,7 +256,7 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.edit_this_file") } else if !ctx.Repo.IsViewBranch { ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.must_be_on_a_branch") - } else if !ctx.Repo.IsWriter() { + } else if !ctx.Repo.CanWrite(models.UnitTypeCode) { ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.fork_before_edit") } @@ -275,16 +275,16 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st ctx.Data["DeleteFileTooltip"] = ctx.Tr("repo.editor.delete_this_file") } else if !ctx.Repo.IsViewBranch { ctx.Data["DeleteFileTooltip"] = ctx.Tr("repo.editor.must_be_on_a_branch") - } else if !ctx.Repo.IsWriter() { + } else if !ctx.Repo.CanWrite(models.UnitTypeCode) { ctx.Data["DeleteFileTooltip"] = ctx.Tr("repo.editor.must_have_write_access") } } // Home render repository home page func Home(ctx *context.Context) { - if len(ctx.Repo.Repository.Units) > 0 { + if len(ctx.Repo.Units) > 0 { var firstUnit *models.Unit - for _, repoUnit := range ctx.Repo.Repository.Units { + for _, repoUnit := range ctx.Repo.Units { if repoUnit.Type == models.UnitTypeCode { renderCode(ctx) return diff --git a/routers/repo/wiki.go b/routers/repo/wiki.go index 3220ab134d..2349236358 100644 --- a/routers/repo/wiki.go +++ b/routers/repo/wiki.go @@ -1,4 +1,5 @@ // Copyright 2015 The Gogs Authors. All rights reserved. +// 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. @@ -30,8 +31,8 @@ const ( // MustEnableWiki check if wiki is enabled, if external then redirect func MustEnableWiki(ctx *context.Context) { - if !ctx.Repo.Repository.UnitEnabled(models.UnitTypeWiki) && - !ctx.Repo.Repository.UnitEnabled(models.UnitTypeExternalWiki) { + if !ctx.Repo.CanRead(models.UnitTypeWiki) && + !ctx.Repo.CanRead(models.UnitTypeExternalWiki) { ctx.NotFound("MustEnableWiki", nil) return } @@ -200,6 +201,7 @@ func renderWikiPage(ctx *context.Context, isViewPage bool) (*git.Repository, *gi // Wiki renders single wiki page func Wiki(ctx *context.Context) { ctx.Data["PageIsWiki"] = true + ctx.Data["CanWriteWiki"] = ctx.Repo.CanWrite(models.UnitTypeWiki) if !ctx.Repo.Repository.HasWiki() { ctx.Data["Title"] = ctx.Tr("repo.wiki") @@ -235,14 +237,15 @@ func Wiki(ctx *context.Context) { // WikiPages render wiki pages list page func WikiPages(ctx *context.Context) { - ctx.Data["Title"] = ctx.Tr("repo.wiki.pages") - ctx.Data["PageIsWiki"] = true - if !ctx.Repo.Repository.HasWiki() { ctx.Redirect(ctx.Repo.RepoLink + "/wiki") return } + ctx.Data["Title"] = ctx.Tr("repo.wiki.pages") + ctx.Data["PageIsWiki"] = true + ctx.Data["CanWriteWiki"] = ctx.Repo.CanWrite(models.UnitTypeWiki) + wikiRepo, commit, err := findWikiRepoCommit(ctx) if err != nil { return diff --git a/routers/routes/routes.go b/routers/routes/routes.go index 1c1bcd8f95..eb5841f593 100644 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -393,7 +393,16 @@ func RegisterRoutes(m *macaron.Macaron) { } reqRepoAdmin := context.RequireRepoAdmin() - reqRepoWriter := context.RequireRepoWriter() + reqRepoCodeWriter := context.RequireRepoWriter(models.UnitTypeCode) + reqRepoCodeReader := context.RequireRepoReader(models.UnitTypeCode) + reqRepoReleaseWriter := context.RequireRepoWriter(models.UnitTypeReleases) + reqRepoReleaseReader := context.RequireRepoReader(models.UnitTypeReleases) + reqRepoWikiWriter := context.RequireRepoWriter(models.UnitTypeWiki) + reqRepoIssueReader := context.RequireRepoReader(models.UnitTypeIssues) + reqRepoPullsWriter := context.RequireRepoWriter(models.UnitTypePullRequests) + reqRepoPullsReader := context.RequireRepoReader(models.UnitTypePullRequests) + reqRepoIssuesOrPullsWriter := context.RequireRepoWriterOr(models.UnitTypeIssues, models.UnitTypePullRequests) + reqRepoIssuesOrPullsReader := context.RequireRepoReaderOr(models.UnitTypeIssues, models.UnitTypePullRequests) // ***** START: Organization ***** m.Group("/org", func() { @@ -463,7 +472,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/fork", func() { m.Combo("/:repoid").Get(repo.Fork). Post(bindIgnErr(auth.CreateRepoForm{}), repo.ForkPost) - }, context.RepoIDAssignment(), context.UnitTypes(), context.LoadRepoUnits(), context.CheckUnit(models.UnitTypeCode)) + }, context.RepoIDAssignment(), context.UnitTypes(), reqRepoCodeReader) }, reqSignIn) m.Group("/:username/:reponame", func() { @@ -514,7 +523,7 @@ func RegisterRoutes(m *macaron.Macaron) { }, func(ctx *context.Context) { ctx.Data["PageIsSettings"] = true }) - }, reqSignIn, context.RepoAssignment(), reqRepoAdmin, context.UnitTypes(), context.LoadRepoUnits(), context.RepoRef()) + }, reqSignIn, context.RepoAssignment(), reqRepoAdmin, context.UnitTypes(), context.RepoRef()) m.Get("/:username/:reponame/action/:action", reqSignIn, context.RepoAssignment(), repo.Action) @@ -522,7 +531,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/issues", func() { m.Combo("/new").Get(context.RepoRef(), repo.NewIssue). Post(bindIgnErr(auth.CreateIssueForm{}), repo.NewIssuePost) - }, context.CheckUnit(models.UnitTypeIssues)) + }, reqRepoIssueReader) // FIXME: should use different URLs but mostly same logic for comments of issue and pull reuqest. // So they can apply their own enable/disable logic on routers. m.Group("/issues", func() { @@ -545,22 +554,22 @@ func RegisterRoutes(m *macaron.Macaron) { m.Post("/reactions/:action", bindIgnErr(auth.ReactionForm{}), repo.ChangeIssueReaction) }) - m.Post("/labels", reqRepoWriter, repo.UpdateIssueLabel) - m.Post("/milestone", reqRepoWriter, repo.UpdateIssueMilestone) - m.Post("/assignee", reqRepoWriter, repo.UpdateIssueAssignee) - m.Post("/status", reqRepoWriter, repo.UpdateIssueStatus) + m.Post("/labels", reqRepoIssuesOrPullsWriter, repo.UpdateIssueLabel) + m.Post("/milestone", reqRepoIssuesOrPullsWriter, repo.UpdateIssueMilestone) + m.Post("/assignee", reqRepoIssuesOrPullsWriter, repo.UpdateIssueAssignee) + m.Post("/status", reqRepoIssuesOrPullsWriter, repo.UpdateIssueStatus) }) m.Group("/comments/:id", func() { m.Post("", repo.UpdateCommentContent) m.Post("/delete", repo.DeleteComment) m.Post("/reactions/:action", bindIgnErr(auth.ReactionForm{}), repo.ChangeCommentReaction) - }, context.CheckAnyUnit(models.UnitTypeIssues, models.UnitTypePullRequests)) + }) m.Group("/labels", func() { m.Post("/new", bindIgnErr(auth.CreateLabelForm{}), repo.NewLabel) m.Post("/edit", bindIgnErr(auth.CreateLabelForm{}), repo.UpdateLabel) m.Post("/delete", repo.DeleteLabel) m.Post("/initialize", bindIgnErr(auth.InitializeLabelsForm{}), repo.InitializeLabels) - }, reqRepoWriter, context.RepoRef(), context.CheckAnyUnit(models.UnitTypeIssues, models.UnitTypePullRequests)) + }, reqRepoIssuesOrPullsWriter, context.RepoRef()) m.Group("/milestones", func() { m.Combo("/new").Get(repo.NewMilestone). Post(bindIgnErr(auth.CreateMilestoneForm{}), repo.NewMilestonePost) @@ -568,9 +577,9 @@ func RegisterRoutes(m *macaron.Macaron) { m.Post("/:id/edit", bindIgnErr(auth.CreateMilestoneForm{}), repo.EditMilestonePost) m.Get("/:id/:action", repo.ChangeMilestonStatus) m.Post("/delete", repo.DeleteMilestone) - }, reqRepoWriter, context.RepoRef(), context.CheckAnyUnit(models.UnitTypeIssues, models.UnitTypePullRequests)) + }, reqRepoIssuesOrPullsWriter, context.RepoRef()) - m.Combo("/compare/*", repo.MustAllowPulls, repo.SetEditorconfigIfExists). + m.Combo("/compare/*", reqRepoCodeReader, reqRepoPullsReader, repo.MustAllowPulls, repo.SetEditorconfigIfExists). Get(repo.SetDiffViewStyle, repo.CompareAndPullRequest). Post(bindIgnErr(auth.CreateIssueForm{}), repo.CompareAndPullRequestPost) @@ -591,7 +600,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Post("/upload-file", repo.UploadFileToServer) m.Post("/upload-remove", bindIgnErr(auth.RemoveUploadFileForm{}), repo.RemoveUploadFileFromServer) }, context.RepoRef(), repo.MustBeEditable, repo.MustBeAbleToUpload) - }, repo.MustBeNotBare, reqRepoWriter) + }, reqRepoCodeWriter, repo.MustBeNotBare) m.Group("/branches", func() { m.Group("/_new/", func() { @@ -601,9 +610,9 @@ func RegisterRoutes(m *macaron.Macaron) { }, bindIgnErr(auth.NewBranchForm{})) m.Post("/delete", repo.DeleteBranchPost) m.Post("/restore", repo.RestoreBranchPost) - }, reqRepoWriter, repo.MustBeNotBare, context.CheckUnit(models.UnitTypeCode)) + }, reqRepoCodeWriter, repo.MustBeNotBare) - }, reqSignIn, context.RepoAssignment(), context.UnitTypes(), context.LoadRepoUnits()) + }, reqSignIn, context.RepoAssignment(), context.UnitTypes()) // Releases m.Group("/:username/:reponame", func() { @@ -614,11 +623,11 @@ func RegisterRoutes(m *macaron.Macaron) { m.Get("/new", repo.NewRelease) m.Post("/new", bindIgnErr(auth.NewReleaseForm{}), repo.NewReleasePost) m.Post("/delete", repo.DeleteRelease) - }, reqSignIn, repo.MustBeNotBare, reqRepoWriter, context.RepoRef()) + }, reqSignIn, repo.MustBeNotBare, reqRepoReleaseWriter, context.RepoRef()) m.Group("/releases", func() { m.Get("/edit/*", repo.EditRelease) m.Post("/edit/*", bindIgnErr(auth.EditReleaseForm{}), repo.EditReleasePost) - }, reqSignIn, repo.MustBeNotBare, reqRepoWriter, func(ctx *context.Context) { + }, reqSignIn, repo.MustBeNotBare, reqRepoReleaseWriter, func(ctx *context.Context) { var err error ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch) if err != nil { @@ -632,7 +641,7 @@ func RegisterRoutes(m *macaron.Macaron) { } ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount }) - }, context.RepoAssignment(), context.UnitTypes(), context.LoadRepoUnits(), context.CheckUnit(models.UnitTypeReleases)) + }, context.RepoAssignment(), context.UnitTypes(), reqRepoReleaseReader) m.Group("/:username/:reponame", func() { m.Post("/topics", repo.TopicsPost) @@ -642,8 +651,8 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("", func() { m.Get("/^:type(issues|pulls)$", repo.RetrieveLabels, repo.Issues) m.Get("/^:type(issues|pulls)$/:index", repo.ViewIssue) - m.Get("/labels/", context.CheckAnyUnit(models.UnitTypeIssues, models.UnitTypePullRequests), repo.RetrieveLabels, repo.Labels) - m.Get("/milestones", context.CheckAnyUnit(models.UnitTypeIssues, models.UnitTypePullRequests), repo.Milestones) + m.Get("/labels/", reqRepoIssuesOrPullsReader, repo.RetrieveLabels, repo.Labels) + m.Get("/milestones", reqRepoIssuesOrPullsReader, repo.Milestones) }, context.RepoRef()) m.Group("/wiki", func() { @@ -656,7 +665,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Combo("/:page/_edit").Get(repo.EditWiki). Post(bindIgnErr(auth.NewWikiForm{}), repo.EditWikiPost) m.Post("/:page/delete", repo.DeleteWikiPagePost) - }, reqSignIn, reqRepoWriter) + }, reqSignIn, reqRepoWikiWriter) }, repo.MustEnableWiki, context.RepoRef()) m.Group("/wiki", func() { @@ -666,19 +675,19 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/activity", func() { m.Get("", repo.Activity) m.Get("/:period", repo.Activity) - }, context.RepoRef(), repo.MustBeNotBare, context.CheckAnyUnit(models.UnitTypePullRequests, models.UnitTypeIssues, models.UnitTypeReleases)) + }, context.RepoRef(), repo.MustBeNotBare, context.RequireRepoReaderOr(models.UnitTypePullRequests, models.UnitTypeIssues, models.UnitTypeReleases)) - m.Get("/archive/*", repo.MustBeNotBare, context.CheckUnit(models.UnitTypeCode), repo.Download) + m.Get("/archive/*", repo.MustBeNotBare, reqRepoCodeReader, repo.Download) m.Group("/branches", func() { m.Get("", repo.Branches) - }, repo.MustBeNotBare, context.RepoRef(), context.CheckUnit(models.UnitTypeCode)) + }, repo.MustBeNotBare, context.RepoRef(), reqRepoCodeReader) m.Group("/pulls/:index", func() { m.Get(".diff", repo.DownloadPullDiff) m.Get(".patch", repo.DownloadPullPatch) m.Get("/commits", context.RepoRef(), repo.ViewPullCommits) - m.Post("/merge", reqRepoWriter, bindIgnErr(auth.MergePullRequestForm{}), repo.MergePullRequest) + m.Post("/merge", reqRepoPullsWriter, bindIgnErr(auth.MergePullRequestForm{}), repo.MergePullRequest) m.Post("/cleanup", context.RepoRef(), repo.CleanUpPullRequest) m.Group("/files", func() { m.Get("", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.ViewPullFiles) @@ -696,7 +705,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Get("/blob/:sha", context.RepoRefByType(context.RepoRefBlob), repo.DownloadByID) // "/*" route is deprecated, and kept for backward compatibility m.Get("/*", context.RepoRefByType(context.RepoRefLegacy), repo.SingleDownload) - }, repo.MustBeNotBare, context.CheckUnit(models.UnitTypeCode)) + }, repo.MustBeNotBare, reqRepoCodeReader) m.Group("/commits", func() { m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.RefCommits) @@ -704,12 +713,12 @@ func RegisterRoutes(m *macaron.Macaron) { m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.RefCommits) // "/*" route is deprecated, and kept for backward compatibility m.Get("/*", context.RepoRefByType(context.RepoRefLegacy), repo.RefCommits) - }, repo.MustBeNotBare, context.CheckUnit(models.UnitTypeCode)) + }, repo.MustBeNotBare, reqRepoCodeReader) m.Group("", func() { m.Get("/graph", repo.Graph) m.Get("/commit/:sha([a-f0-9]{7,40})$", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.Diff) - }, repo.MustBeNotBare, context.RepoRef(), context.CheckUnit(models.UnitTypeCode)) + }, repo.MustBeNotBare, context.RepoRef(), reqRepoCodeReader) m.Group("/src", func() { m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.Home) @@ -721,24 +730,24 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("", func() { m.Get("/forks", repo.Forks) - }, context.RepoRef(), context.CheckUnit(models.UnitTypeCode)) + }, context.RepoRef(), reqRepoCodeReader) m.Get("/commit/:sha([a-f0-9]{7,40})\\.:ext(patch|diff)", - repo.MustBeNotBare, context.CheckUnit(models.UnitTypeCode), repo.RawDiff) + repo.MustBeNotBare, reqRepoCodeReader, repo.RawDiff) m.Get("/compare/:before([a-z0-9]{40})\\.\\.\\.:after([a-z0-9]{40})", repo.SetEditorconfigIfExists, - repo.SetDiffViewStyle, repo.MustBeNotBare, context.CheckUnit(models.UnitTypeCode), repo.CompareDiff) - }, ignSignIn, context.RepoAssignment(), context.UnitTypes(), context.LoadRepoUnits()) + repo.SetDiffViewStyle, repo.MustBeNotBare, reqRepoCodeReader, repo.CompareDiff) + }, ignSignIn, context.RepoAssignment(), context.UnitTypes()) m.Group("/:username/:reponame", func() { m.Get("/stars", repo.Stars) m.Get("/watchers", repo.Watchers) - m.Get("/search", context.CheckUnit(models.UnitTypeCode), repo.Search) - }, ignSignIn, context.RepoAssignment(), context.RepoRef(), context.UnitTypes(), context.LoadRepoUnits()) + m.Get("/search", reqRepoCodeReader, repo.Search) + }, ignSignIn, context.RepoAssignment(), context.RepoRef(), context.UnitTypes()) m.Group("/:username", func() { m.Group("/:reponame", func() { m.Get("", repo.SetEditorconfigIfExists, repo.Home) m.Get("\\.git$", repo.SetEditorconfigIfExists, repo.Home) - }, ignSignIn, context.RepoAssignment(), context.RepoRef(), context.UnitTypes(), context.LoadRepoUnits()) + }, ignSignIn, context.RepoAssignment(), context.RepoRef(), context.UnitTypes()) m.Group("/:reponame", func() { m.Group("\\.git/info/lfs", func() { diff --git a/routers/user/home.go b/routers/user/home.go index 49c6579556..4c6a9f6cd0 100644 --- a/routers/user/home.go +++ b/routers/user/home.go @@ -286,7 +286,12 @@ func Issues(ctx *context.Context) { repo := showReposMap[repoID] // Check if user has access to given repository. - if !repo.IsOwnedBy(ctxUser.ID) && !repo.HasAccess(ctxUser) { + perm, err := models.GetUserRepoPermission(repo, ctxUser) + if err != nil { + ctx.ServerError("GetUserRepoPermission", fmt.Errorf("[%d]%v", repoID, err)) + return + } + if !perm.CanRead(models.UnitTypeIssues) { ctx.Status(404) return } |