Fix get release draft permission check (#36659)

Draft release and it's attachments need a write permission to access.

---------

Signed-off-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Giteabot <teabot@gitea.io>
This commit is contained in:
Lunny Xiao
2026-02-22 12:56:46 -08:00
committed by GitHub
parent 5f8e19fcef
commit 1eced4a7c0
5 changed files with 174 additions and 3 deletions

View File

@@ -8,6 +8,7 @@ import (
"fmt"
"net/http"
auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/perm"
repo_model "code.gitea.io/gitea/models/repo"
@@ -21,6 +22,28 @@ import (
release_service "code.gitea.io/gitea/services/release"
)
func hasRepoWriteScope(ctx *context.APIContext) bool {
scope, ok := ctx.Data["ApiTokenScope"].(auth_model.AccessTokenScope)
if ctx.Data["IsApiToken"] != true || !ok {
return true
}
requiredScopes := auth_model.GetRequiredScopes(auth_model.Write, auth_model.AccessTokenScopeCategoryRepository)
allow, err := scope.HasScope(requiredScopes...)
if err != nil {
ctx.APIError(http.StatusForbidden, "checking scope failed: "+err.Error())
return false
}
return allow
}
func canAccessDraftRelease(ctx *context.APIContext) bool {
if !ctx.IsSigned || !ctx.Repo.CanWrite(unit.TypeReleases) {
return false
}
return hasRepoWriteScope(ctx)
}
// GetRelease get a single release of a repository
func GetRelease(ctx *context.APIContext) {
// swagger:operation GET /repos/{owner}/{repo}/releases/{id} repository repoGetRelease
@@ -62,6 +85,15 @@ func GetRelease(ctx *context.APIContext) {
return
}
if release.IsDraft { // only the users with write access can see draft releases
if !canAccessDraftRelease(ctx) {
if !ctx.Written() {
ctx.APIErrorNotFound()
}
return
}
}
if err := release.LoadAttributes(ctx); err != nil {
ctx.APIErrorInternal(err)
return
@@ -151,9 +183,13 @@ func ListReleases(ctx *context.APIContext) {
// "$ref": "#/responses/notFound"
listOptions := utils.GetListOptions(ctx)
includeDrafts := (ctx.Repo.AccessMode >= perm.AccessModeWrite || ctx.Repo.UnitAccessMode(unit.TypeReleases) >= perm.AccessModeWrite) && hasRepoWriteScope(ctx)
if ctx.Written() {
return
}
opts := repo_model.FindReleasesOptions{
ListOptions: listOptions,
IncludeDrafts: ctx.Repo.AccessMode >= perm.AccessModeWrite || ctx.Repo.UnitAccessMode(unit.TypeReleases) >= perm.AccessModeWrite,
IncludeDrafts: includeDrafts,
IncludeTags: false,
IsDraft: ctx.FormOptionalBool("draft"),
IsPreRelease: ctx.FormOptionalBool("pre-release"),

View File

@@ -34,6 +34,14 @@ func checkReleaseMatchRepo(ctx *context.APIContext, releaseID int64) bool {
ctx.APIErrorNotFound()
return false
}
if release.IsDraft {
if !canAccessDraftRelease(ctx) {
if !ctx.Written() {
ctx.APIErrorNotFound()
}
return false
}
}
return true
}
@@ -141,6 +149,14 @@ func ListReleaseAttachments(ctx *context.APIContext) {
ctx.APIErrorNotFound()
return
}
if release.IsDraft {
if !canAccessDraftRelease(ctx) {
if !ctx.Written() {
ctx.APIErrorNotFound()
}
return
}
}
if err := release.LoadAttributes(ctx); err != nil {
ctx.APIErrorInternal(err)
return