refactor: use named Permission field in Repository struct instead of anonymous embedding (#37441)

The `Repository` struct in `services/context/repo.go` embedded
`access_model.Permission` anonymously, causing all permission methods to
be promoted directly onto `Repository`. This made it unclear at call
sites whether a method belonged to `Repository` itself or to its
embedded `Permission`.

### Changes

- **`services/context/repo.go`**: Replace anonymous
`access_model.Permission` with named field `Permission
access_model.Permission`
- **49 files** updated to route permission method calls through the
named field:

```go
// Before
ctx.Repo.IsAdmin()
ctx.Repo.CanWrite(unit.TypeCode)
ctx.Repo.CanReadIssuesOrPulls(isPull)
slices.ContainsFunc(unitTypes, ctx.Repo.CanWrite)

// After
ctx.Repo.Permission.IsAdmin()
ctx.Repo.Permission.CanWrite(unit.TypeCode)
ctx.Repo.Permission.CanReadIssuesOrPulls(isPull)
slices.ContainsFunc(unitTypes, ctx.Repo.Permission.CanWrite)
```

Methods defined directly on `*Repository` (`CanWriteToBranch`,
`CanCreateBranch`, etc.) are unchanged.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: wxiaoguang <2114189+wxiaoguang@users.noreply.github.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Nicolas <bircni@icloud.com>
This commit is contained in:
Copilot
2026-04-26 20:18:28 +00:00
committed by GitHub
parent 55c9b936cb
commit 45b4fffae4
49 changed files with 169 additions and 171 deletions

View File

@@ -81,7 +81,7 @@ func MustAllowUserComment(ctx *context.Context) {
return
}
if issue.IsLocked && !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) && !ctx.Doer.IsAdmin {
if issue.IsLocked && !ctx.Repo.Permission.CanWriteIssuesOrPulls(issue.IsPull) && !ctx.Doer.IsAdmin {
ctx.Flash.Error(ctx.Tr("repo.issues.comment_on_locked"))
ctx.Redirect(issue.Link())
return
@@ -90,8 +90,8 @@ func MustAllowUserComment(ctx *context.Context) {
// MustEnableIssues check if repository enable internal issues
func MustEnableIssues(ctx *context.Context) {
if !ctx.Repo.CanRead(unit.TypeIssues) &&
!ctx.Repo.CanRead(unit.TypeExternalTracker) {
if !ctx.Repo.Permission.CanRead(unit.TypeIssues) &&
!ctx.Repo.Permission.CanRead(unit.TypeExternalTracker) {
ctx.NotFound(nil)
return
}
@@ -105,7 +105,7 @@ func MustEnableIssues(ctx *context.Context) {
// MustAllowPulls check if repository enable pull requests and user have right to do that
func MustAllowPulls(ctx *context.Context) {
if !ctx.Repo.Repository.CanEnablePulls() || !ctx.Repo.CanRead(unit.TypePullRequests) {
if !ctx.Repo.Repository.CanEnablePulls() || !ctx.Repo.Permission.CanRead(unit.TypePullRequests) {
ctx.NotFound(nil)
return
}
@@ -195,8 +195,8 @@ func GetActionIssue(ctx *context.Context) *issues_model.Issue {
}
func checkIssueRights(ctx *context.Context, issue *issues_model.Issue) {
if issue.IsPull && !ctx.Repo.CanRead(unit.TypePullRequests) ||
!issue.IsPull && !ctx.Repo.CanRead(unit.TypeIssues) {
if issue.IsPull && !ctx.Repo.Permission.CanRead(unit.TypePullRequests) ||
!issue.IsPull && !ctx.Repo.Permission.CanRead(unit.TypeIssues) {
ctx.NotFound(nil)
}
}
@@ -221,8 +221,8 @@ func getActionIssues(ctx *context.Context) issues_model.IssueList {
return nil
}
// Check access rights for all issues
issueUnitEnabled := ctx.Repo.CanRead(unit.TypeIssues)
prUnitEnabled := ctx.Repo.CanRead(unit.TypePullRequests)
issueUnitEnabled := ctx.Repo.Permission.CanRead(unit.TypeIssues)
prUnitEnabled := ctx.Repo.Permission.CanRead(unit.TypePullRequests)
for _, issue := range issues {
if issue.RepoID != ctx.Repo.Repository.ID {
ctx.NotFound(errors.New("some issue's RepoID is incorrect"))
@@ -254,13 +254,13 @@ func GetIssueInfo(ctx *context.Context) {
if issue.IsPull {
// Need to check if Pulls are enabled and we can read Pulls
if !ctx.Repo.Repository.CanEnablePulls() || !ctx.Repo.CanRead(unit.TypePullRequests) {
if !ctx.Repo.Repository.CanEnablePulls() || !ctx.Repo.Permission.CanRead(unit.TypePullRequests) {
ctx.HTTPError(http.StatusNotFound)
return
}
} else {
// Need to check if Issues are enabled and we can read Issues
if !ctx.Repo.CanRead(unit.TypeIssues) {
if !ctx.Repo.Permission.CanRead(unit.TypeIssues) {
ctx.HTTPError(http.StatusNotFound)
return
}
@@ -279,7 +279,7 @@ func UpdateIssueTitle(ctx *context.Context) {
return
}
if !ctx.IsSigned || (!issue.IsPoster(ctx.Doer.ID) && !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull)) {
if !ctx.IsSigned || (!issue.IsPoster(ctx.Doer.ID) && !ctx.Repo.Permission.CanWriteIssuesOrPulls(issue.IsPull)) {
ctx.HTTPError(http.StatusForbidden)
return
}
@@ -307,7 +307,7 @@ func UpdateIssueRef(ctx *context.Context) {
return
}
if !ctx.IsSigned || (!issue.IsPoster(ctx.Doer.ID) && !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull)) || issue.IsPull {
if !ctx.IsSigned || (!issue.IsPoster(ctx.Doer.ID) && !ctx.Repo.Permission.CanWriteIssuesOrPulls(issue.IsPull)) || issue.IsPull {
ctx.HTTPError(http.StatusForbidden)
return
}
@@ -331,7 +331,7 @@ func UpdateIssueContent(ctx *context.Context) {
return
}
if !ctx.IsSigned || (ctx.Doer.ID != issue.PosterID && !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull)) {
if !ctx.IsSigned || (ctx.Doer.ID != issue.PosterID && !ctx.Repo.Permission.CanWriteIssuesOrPulls(issue.IsPull)) {
ctx.HTTPError(http.StatusForbidden)
return
}
@@ -387,7 +387,7 @@ func UpdateIssueDeadline(ctx *context.Context) {
return
}
if !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) {
if !ctx.Repo.Permission.CanWriteIssuesOrPulls(issue.IsPull) {
ctx.HTTPError(http.StatusForbidden, "", "Not repo writer")
return
}
@@ -486,7 +486,7 @@ func ChangeIssueReaction(ctx *context.Context) {
return
}
if !ctx.IsSigned || (ctx.Doer.ID != issue.PosterID && !ctx.Repo.CanReadIssuesOrPulls(issue.IsPull)) {
if !ctx.IsSigned || (ctx.Doer.ID != issue.PosterID && !ctx.Repo.Permission.CanReadIssuesOrPulls(issue.IsPull)) {
if log.IsTrace() {
if ctx.IsSigned {
issueType := "issues"