mirror of
https://github.com/go-gitea/gitea.git
synced 2026-08-29 10:14:11 +00:00
fix(repo): centralize repository-scoped authorization (#39063)
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
+11
-2
@@ -427,6 +427,15 @@ func reqOwner() func(ctx *context.APIContext) {
|
||||
}
|
||||
}
|
||||
|
||||
func reqRepoDangerZone() func(ctx *context.APIContext) {
|
||||
return func(ctx *context.APIContext) {
|
||||
if !access_model.CanDoerManageRepoDangerZone(ctx, ctx.Doer, ctx.Repo.Repository, &ctx.Repo.Permission) {
|
||||
ctx.APIError(http.StatusForbidden, "user has no permission to manage the danger zone")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// reqSelfOrAdmin doer should be the same as the contextUser or site admin
|
||||
func reqSelfOrAdmin() func(ctx *context.APIContext) {
|
||||
return func(ctx *context.APIContext) {
|
||||
@@ -1305,11 +1314,11 @@ func Routes() *web.Router {
|
||||
m.Get("/compare/*", reqRepoReader(unit.TypeCode), repo.CompareDiff)
|
||||
|
||||
m.Combo("").Get(reqAnyRepoReader(), repo.Get).
|
||||
Delete(reqToken(), reqOwner(), repo.Delete).
|
||||
Delete(reqToken(), reqRepoDangerZone(), repo.Delete).
|
||||
Patch(reqToken(), reqAdmin(), bind(api.EditRepoOption{}), repo.Edit)
|
||||
m.Post("/generate", reqToken(), reqRepoReader(unit.TypeCode), bind(api.GenerateRepoOption{}), repo.Generate)
|
||||
m.Group("/transfer", func() {
|
||||
m.Post("", reqOwner(), bind(api.TransferRepoOption{}), repo.Transfer)
|
||||
m.Post("", reqRepoDangerZone(), bind(api.TransferRepoOption{}), repo.Transfer)
|
||||
m.Post("/accept", repo.AcceptTransfer)
|
||||
m.Post("/reject", repo.RejectTransfer)
|
||||
}, reqToken())
|
||||
|
||||
+18
-40
@@ -613,7 +613,7 @@ func GetTeamRepo(ctx *context.APIContext) {
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
repo := getRepositoryByParams(ctx)
|
||||
repo, permission := getRepositoryByParams(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
@@ -629,11 +629,6 @@ func GetTeamRepo(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
permission, err := access_model.GetDoerRepoPermission(ctx, repo, ctx.Doer)
|
||||
if err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return
|
||||
}
|
||||
// The team may be reachable by a non-team-member via its visibility tier;
|
||||
// don't confirm the existence of a repo the doer cannot access.
|
||||
if !permission.HasAnyUnitAccessOrPublicAccess() {
|
||||
@@ -641,31 +636,28 @@ func GetTeamRepo(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, convert.ToRepo(ctx, repo, permission))
|
||||
ctx.JSON(http.StatusOK, convert.ToRepo(ctx, repo, *permission))
|
||||
}
|
||||
|
||||
// getRepositoryByParams get repository by a team's organization ID and repo name
|
||||
func getRepositoryByParams(ctx *context.APIContext) *repo_model.Repository {
|
||||
func getRepositoryByParams(ctx *context.APIContext) (*repo_model.Repository, *access_model.Permission) {
|
||||
repo, err := repo_model.GetRepositoryByName(ctx, ctx.Org.Team.OrgID, ctx.PathParam("reponame"))
|
||||
if err != nil {
|
||||
if repo_model.IsErrRepoNotExist(err) {
|
||||
ctx.APIErrorNotFound()
|
||||
} else {
|
||||
ctx.APIErrorInternal(err)
|
||||
}
|
||||
return nil
|
||||
ctx.APIErrorAuto(err)
|
||||
return nil, nil
|
||||
}
|
||||
return repo
|
||||
perm, err := access_model.GetDoerRepoPermission(ctx, repo, ctx.Doer)
|
||||
if err != nil {
|
||||
ctx.APIErrorAuto(err)
|
||||
return nil, nil
|
||||
}
|
||||
return repo, &perm
|
||||
}
|
||||
|
||||
func canChangeTeamRepository(ctx *context.APIContext) bool {
|
||||
canChange, err := ctx.Org.Organization.CanChangeRepoTeamAccess(ctx, ctx.Doer)
|
||||
if err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return false
|
||||
}
|
||||
func canManageRepoCollaboratorTeam(ctx *context.APIContext, repo *repo_model.Repository, perm *access_model.Permission) bool {
|
||||
canChange := access_model.CanDoerManageOrgRepoCollaboratorTeam(ctx, repo, perm)
|
||||
if !canChange {
|
||||
ctx.APIError(http.StatusForbidden, "Must be an organization owner")
|
||||
ctx.APIError(http.StatusForbidden, "Must have permission to manage team repository access")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
@@ -703,18 +695,11 @@ func AddTeamRepository(ctx *context.APIContext) {
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
repo := getRepositoryByParams(ctx)
|
||||
repo, perm := getRepositoryByParams(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
if !canChangeTeamRepository(ctx) {
|
||||
return
|
||||
}
|
||||
if access, err := access_model.AccessLevel(ctx, ctx.Doer, repo); err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return
|
||||
} else if access < perm.AccessModeAdmin {
|
||||
ctx.APIError(http.StatusForbidden, "Must have admin-level access to the repository")
|
||||
if !canManageRepoCollaboratorTeam(ctx, repo, perm) {
|
||||
return
|
||||
}
|
||||
if err := repo_service.TeamAddRepository(ctx, ctx.Org.Team, repo); err != nil {
|
||||
@@ -758,18 +743,11 @@ func RemoveTeamRepository(ctx *context.APIContext) {
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
repo := getRepositoryByParams(ctx)
|
||||
repo, perm := getRepositoryByParams(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
if !canChangeTeamRepository(ctx) {
|
||||
return
|
||||
}
|
||||
if access, err := access_model.AccessLevel(ctx, ctx.Doer, repo); err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return
|
||||
} else if access < perm.AccessModeAdmin {
|
||||
ctx.APIError(http.StatusForbidden, "Must have admin-level access to the repository")
|
||||
if !canManageRepoCollaboratorTeam(ctx, repo, perm) {
|
||||
return
|
||||
}
|
||||
if err := repo_service.RemoveRepositoryFromTeam(ctx, ctx.Org.Team, repo.ID); err != nil {
|
||||
|
||||
@@ -1154,15 +1154,6 @@ func Delete(ctx *context.APIContext) {
|
||||
owner := ctx.Repo.Owner
|
||||
repo := ctx.Repo.Repository
|
||||
|
||||
canDelete, err := repo_module.CanUserDelete(ctx, repo, ctx.Doer)
|
||||
if err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return
|
||||
} else if !canDelete {
|
||||
ctx.APIError(http.StatusForbidden, "Given user is not owner of organization.")
|
||||
return
|
||||
}
|
||||
|
||||
if ctx.Repo.GitRepo != nil {
|
||||
ctx.Repo.GitRepo.Close()
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"net/http"
|
||||
|
||||
"gitea.dev/models/organization"
|
||||
access_model "gitea.dev/models/perm/access"
|
||||
"gitea.dev/services/context"
|
||||
"gitea.dev/services/convert"
|
||||
repo_service "gitea.dev/services/repository"
|
||||
@@ -188,11 +189,7 @@ func DeleteTeam(ctx *context.APIContext) {
|
||||
}
|
||||
|
||||
func changeRepoTeam(ctx *context.APIContext, add bool) {
|
||||
if !ctx.Repo.Owner.IsOrganization() {
|
||||
ctx.APIError(http.StatusMethodNotAllowed, "repo is not owned by an organization")
|
||||
return
|
||||
}
|
||||
if !canChangeRepoTeam(ctx) {
|
||||
if !canChangeOrgRepoTeam(ctx) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -224,14 +221,10 @@ func changeRepoTeam(ctx *context.APIContext, add bool) {
|
||||
ctx.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func canChangeRepoTeam(ctx *context.APIContext) bool {
|
||||
canChange, err := organization.OrgFromUser(ctx.Repo.Owner).CanChangeRepoTeamAccess(ctx, ctx.Doer)
|
||||
if err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return false
|
||||
}
|
||||
func canChangeOrgRepoTeam(ctx *context.APIContext) bool {
|
||||
canChange := access_model.CanDoerManageOrgRepoCollaboratorTeam(ctx, ctx.Repo.Repository, &ctx.Repo.Permission)
|
||||
if !canChange {
|
||||
ctx.APIError(http.StatusForbidden, "Must be an organization owner")
|
||||
ctx.APIError(http.StatusForbidden, "No permission to change organization repository's team")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
|
||||
Reference in New Issue
Block a user