fix(repo): centralize repository-scoped authorization (#39063)

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
bircni
2026-08-26 20:12:53 +08:00
committed by GitHub
co-authored by wxiaoguang
parent 90c43e8e78
commit e21c37703e
27 changed files with 383 additions and 476 deletions
+40 -30
View File
@@ -13,6 +13,7 @@ import (
"gitea.dev/models/db"
"gitea.dev/models/organization"
access_model "gitea.dev/models/perm/access"
repo_model "gitea.dev/models/repo"
unit_model "gitea.dev/models/unit"
user_model "gitea.dev/models/user"
@@ -55,6 +56,14 @@ type selectOption struct {
Selected bool
}
func canManageRepoDangerZone(ctx *context.Context) bool {
if !access_model.CanDoerManageRepoDangerZone(ctx, ctx.Doer, ctx.Repo.Repository, &ctx.Repo.Permission) {
ctx.JSONErrorNotFound()
return false
}
return true
}
// SettingsCtxData is a middleware that sets all the general context data for the
// settings template.
func SettingsCtxData(ctx *context.Context) {
@@ -67,6 +76,7 @@ func SettingsCtxData(ctx *context.Context) {
ctx.Data["DefaultMirrorInterval"] = setting.Mirror.DefaultInterval
ctx.Data["MinimumMirrorInterval"] = setting.Mirror.MinInterval
ctx.Data["CanConvertFork"] = ctx.Repo.Repository.IsFork && ctx.Doer.CanCreateRepoIn(ctx.Repo.Repository.Owner)
ctx.Data["CanManagerDangerZone"] = access_model.CanDoerManageRepoDangerZone(ctx, ctx.Doer, ctx.Repo.Repository, &ctx.Repo.Permission)
signing, _ := git.GetSigningKey(ctx)
ctx.Data["SigningKeyAvailable"] = signing != nil
@@ -774,12 +784,12 @@ func handleSettingsPostAdminIndex(ctx *context.Context) {
}
func handleSettingsPostConvert(ctx *context.Context) {
form := web.GetForm[*forms.RepoSettingForm](ctx)
repo := ctx.Repo.Repository
if !ctx.Repo.Permission.IsOwner() {
ctx.JSONErrorNotFound()
if !canManageRepoDangerZone(ctx) {
return
}
form := web.GetForm[*forms.RepoSettingForm](ctx)
repo := ctx.Repo.Repository
if repo.Name != form.RepoName {
ctx.JSONError(ctx.Tr("form.enterred_invalid_repo_name"))
return
@@ -804,12 +814,12 @@ func handleSettingsPostConvert(ctx *context.Context) {
}
func handleSettingsPostConvertFork(ctx *context.Context) {
form := web.GetForm[*forms.RepoSettingForm](ctx)
repo := ctx.Repo.Repository
if !ctx.Repo.Permission.IsOwner() {
ctx.JSONErrorNotFound()
if !canManageRepoDangerZone(ctx) {
return
}
form := web.GetForm[*forms.RepoSettingForm](ctx)
repo := ctx.Repo.Repository
if err := repo.LoadOwner(ctx); err != nil {
ctx.ServerError("Convert Fork", err)
return
@@ -844,12 +854,12 @@ func handleSettingsPostConvertFork(ctx *context.Context) {
}
func handleSettingsPostTransfer(ctx *context.Context) {
form := web.GetForm[*forms.RepoSettingForm](ctx)
repo := ctx.Repo.Repository
if !ctx.Repo.Permission.IsOwner() {
ctx.JSONErrorNotFound()
if !canManageRepoDangerZone(ctx) {
return
}
form := web.GetForm[*forms.RepoSettingForm](ctx)
repo := ctx.Repo.Repository
if repo.Name != form.RepoName {
ctx.JSONError(ctx.Tr("form.enterred_invalid_repo_name"))
return
@@ -908,12 +918,11 @@ func handleSettingsPostTransfer(ctx *context.Context) {
}
func handleSettingsPostCancelTransfer(ctx *context.Context) {
repo := ctx.Repo.Repository
if !ctx.Repo.Permission.IsOwner() {
ctx.HTTPError(http.StatusNotFound)
if !canManageRepoDangerZone(ctx) {
return
}
repo := ctx.Repo.Repository
repoTransfer, err := repo_model.GetPendingRepositoryTransfer(ctx, ctx.Repo.Repository)
if err != nil {
if repo_model.IsErrNoPendingTransfer(err) {
@@ -936,12 +945,12 @@ func handleSettingsPostCancelTransfer(ctx *context.Context) {
}
func handleSettingsPostDelete(ctx *context.Context) {
form := web.GetForm[*forms.RepoSettingForm](ctx)
repo := ctx.Repo.Repository
if !ctx.Repo.Permission.IsOwner() {
ctx.JSONErrorNotFound()
if !canManageRepoDangerZone(ctx) {
return
}
form := web.GetForm[*forms.RepoSettingForm](ctx)
repo := ctx.Repo.Repository
if repo.Name != form.RepoName {
ctx.JSONError(ctx.Tr("form.enterred_invalid_repo_name"))
return
@@ -963,12 +972,11 @@ func handleSettingsPostDelete(ctx *context.Context) {
}
func handleSettingsPostDeleteWiki(ctx *context.Context) {
form := web.GetForm[*forms.RepoSettingForm](ctx)
repo := ctx.Repo.Repository
if !ctx.Repo.Permission.IsOwner() {
ctx.JSONErrorNotFound()
if !canManageRepoDangerZone(ctx) {
return
}
form := web.GetForm[*forms.RepoSettingForm](ctx)
repo := ctx.Repo.Repository
if repo.Name != form.RepoName {
ctx.JSONError(ctx.Tr("form.enterred_invalid_repo_name"))
return
@@ -985,12 +993,11 @@ func handleSettingsPostDeleteWiki(ctx *context.Context) {
}
func handleSettingsPostArchive(ctx *context.Context) {
repo := ctx.Repo.Repository
if !ctx.Repo.Permission.IsOwner() {
ctx.HTTPError(http.StatusForbidden)
if !canManageRepoDangerZone(ctx) {
return
}
repo := ctx.Repo.Repository
if repo.IsMirror {
ctx.Flash.Error(ctx.Tr("repo.settings.archive.error_ismirror"))
ctx.Redirect(ctx.Repo.RepoLink + "/settings")
@@ -1018,12 +1025,11 @@ func handleSettingsPostArchive(ctx *context.Context) {
}
func handleSettingsPostUnarchive(ctx *context.Context) {
repo := ctx.Repo.Repository
if !ctx.Repo.Permission.IsOwner() {
ctx.HTTPError(http.StatusForbidden)
if !canManageRepoDangerZone(ctx) {
return
}
repo := ctx.Repo.Repository
if err := repo_model.SetArchiveRepoState(ctx, repo, false); err != nil {
log.Error("Tried to unarchive a repo: %s", err)
ctx.Flash.Error(ctx.Tr("repo.settings.unarchive.error"))
@@ -1047,6 +1053,10 @@ func handleSettingsPostUnarchive(ctx *context.Context) {
}
func handleSettingsPostVisibility(ctx *context.Context) {
if !canManageRepoDangerZone(ctx) {
return
}
repo := ctx.Repo.Repository
if repo.IsFork {
ctx.JSONError(ctx.Tr("repo.settings.visibility.fork_error"))
@@ -1055,7 +1065,7 @@ func handleSettingsPostVisibility(ctx *context.Context) {
private := ctx.FormOptionalBool("private").ValueOrDefault(true) // default to true for privacy & safety
// when ForcePrivate enabled, you could change public repo to private, but only admin users can change private to public
// when ForcePrivate enabled, you could change public repo to private, only site admin users can change private to public
if !private && setting.Repository.ForcePrivate && !ctx.Doer.IsAdmin {
ctx.JSONError(ctx.Tr("form.repository_force_private"))
return