fix(packages): restrict/limited/token-scope access (#39041, #39043, #39044, #39047, #39046) (#39058)

This commit is contained in:
Giteabot
2026-08-28 00:34:49 +08:00
committed by GitHub
parent 068355cabd
commit 1dab66b83c
21 changed files with 219 additions and 74 deletions
+1 -1
View File
@@ -1701,7 +1701,7 @@ func Routes() *web.Router {
m.Get("/{org}/permissions", reqToken(), org.GetUserOrgsPermissions)
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser, auth_model.AccessTokenScopeCategoryOrganization), context.UserAssignmentAPI(), checkTokenPublicOnly(), individualPermsChecker)
m.Post("/orgs", tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), reqToken(), bind(api.CreateOrgOption{}), org.Create)
m.Get("/orgs", org.GetAll, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization))
m.Get("/orgs", tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), org.GetAll)
m.Group("/orgs/{org}", func() {
m.Combo("").Get(org.Get).
Patch(reqToken(), reqOrgOwnership(), bind(api.EditOrgOption{}), org.Edit).
+3 -6
View File
@@ -683,16 +683,13 @@ func getRepositoryByParams(ctx *context.APIContext) *repo_model.Repository {
}
func canChangeTeamRepository(ctx *context.APIContext) bool {
if ctx.Org.Organization.RepoAdminChangeTeamAccess {
return true
}
isOwner, err := ctx.Org.Organization.IsOwnedBy(ctx, ctx.Doer.ID)
canChange, err := ctx.Org.Organization.CanChangeRepoTeamAccess(ctx, ctx.Doer)
if err != nil {
ctx.APIErrorInternal(err)
return false
}
if !isOwner {
ctx.APIError(http.StatusForbidden, "user is nor repo admin nor owner")
if !canChange {
ctx.APIError(http.StatusForbidden, "Must be an organization owner")
return false
}
return true
+19 -2
View File
@@ -137,6 +137,8 @@ func AddTeam(ctx *context.APIContext) {
// responses:
// "204":
// "$ref": "#/responses/empty"
// "403":
// "$ref": "#/responses/forbidden"
// "422":
// "$ref": "#/responses/validationError"
// "405":
@@ -173,6 +175,8 @@ func DeleteTeam(ctx *context.APIContext) {
// responses:
// "204":
// "$ref": "#/responses/empty"
// "403":
// "$ref": "#/responses/forbidden"
// "422":
// "$ref": "#/responses/validationError"
// "405":
@@ -186,9 +190,9 @@ 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 !ctx.Repo.Owner.RepoAdminChangeTeamAccess && !ctx.Repo.Permission.IsOwner() {
ctx.APIError(http.StatusForbidden, "user is nor repo admin nor owner")
if !canChangeRepoTeam(ctx) {
return
}
@@ -220,6 +224,19 @@ 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
}
if !canChange {
ctx.APIError(http.StatusForbidden, "Must be an organization owner")
return false
}
return true
}
func getTeamByParam(ctx *context.APIContext) *organization.Team {
team, err := organization.GetTeam(ctx, ctx.Repo.Owner.ID, ctx.PathParam("team"))
if err != nil {
+6
View File
@@ -10,6 +10,7 @@ import (
"strings"
actions_model "gitea.dev/models/actions"
auth_model "gitea.dev/models/auth"
"gitea.dev/modules/badge"
"gitea.dev/modules/git"
"gitea.dev/modules/util"
@@ -17,6 +18,11 @@ import (
)
func GetWorkflowBadge(ctx *context.Context) {
context.CheckRepoScopedToken(ctx, ctx.Repo.Repository, auth_model.Read)
if ctx.Written() {
return
}
workflowFile := ctx.PathParam("workflow_name")
branch := ctx.FormString("branch", ctx.Repo.Repository.DefaultBranch)
event := ctx.FormString("event")
+22 -6
View File
@@ -43,6 +43,13 @@ func Collaboration(ctx *context.Context) {
ctx.Data["OrgName"] = ctx.Repo.Repository.OwnerName
ctx.Data["Org"] = ctx.Repo.Repository.Owner
ctx.Data["Units"] = unit_model.Units
if ctx.Repo.Owner.IsOrganization() {
ctx.Data["CanChangeRepoTeamAccess"], err = organization.OrgFromUser(ctx.Repo.Owner).CanChangeRepoTeamAccess(ctx, ctx.Doer)
if err != nil {
ctx.ServerError("CanChangeRepoTeamAccess", err)
return
}
}
ctx.HTML(http.StatusOK, tplCollaboration)
}
@@ -155,9 +162,7 @@ func DeleteCollaboration(ctx *context.Context) {
// AddTeamPost response for adding a team to a repository
func AddTeamPost(ctx *context.Context) {
if !ctx.Repo.Owner.RepoAdminChangeTeamAccess && !ctx.Repo.Permission.IsOwner() {
ctx.Flash.Error(ctx.Tr("repo.settings.change_team_access_not_allowed"))
ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration")
if !canChangeRepoTeamAccess(ctx) {
return
}
@@ -201,9 +206,7 @@ func AddTeamPost(ctx *context.Context) {
// DeleteTeam response for deleting a team from a repository
func DeleteTeam(ctx *context.Context) {
if !ctx.Repo.Owner.RepoAdminChangeTeamAccess && !ctx.Repo.Permission.IsOwner() {
ctx.Flash.Error(ctx.Tr("repo.settings.change_team_access_not_allowed"))
ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration")
if !canChangeRepoTeamAccess(ctx) {
return
}
@@ -221,3 +224,16 @@ func DeleteTeam(ctx *context.Context) {
ctx.Flash.Success(ctx.Tr("repo.settings.remove_team_success"))
ctx.JSONRedirect(ctx.Repo.RepoLink + "/settings/collaboration")
}
func canChangeRepoTeamAccess(ctx *context.Context) bool {
canChange, err := organization.OrgFromUser(ctx.Repo.Owner).CanChangeRepoTeamAccess(ctx, ctx.Doer)
if err != nil {
ctx.ServerError("CanChangeRepoTeamAccess", err)
return false
}
if !canChange {
ctx.Flash.Error(ctx.Tr("repo.settings.change_team_access_not_allowed"))
ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration")
}
return canChange
}
+17 -30
View File
@@ -240,40 +240,27 @@ func TestAddTeamPost(t *testing.T) {
func TestAddTeamPost_NotAllowed(t *testing.T) {
unittest.PrepareTestEnv(t)
ctx, _ := contexttest.MockContext(t, "org26/repo43")
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 32})
require.NoError(t, repo.LoadOwner(t.Context()))
adminTeam := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 12})
targetTeam := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 2})
require.NoError(t, repo_service.TeamAddRepository(t.Context(), adminTeam, repo))
doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 28})
repoContext := &context.Repository{Owner: repo.Owner, Repository: repo}
renderCtx, _ := contexttest.MockContext(t, repo.Link()+"/settings/collaboration")
renderCtx.Repo = repoContext
renderCtx.Doer = doer
Collaboration(renderCtx)
assert.Equal(t, false, renderCtx.Data["CanChangeRepoTeamAccess"])
ctx.Req.Form.Set("team", "team11")
org := &user_model.User{
LowerName: "org26",
Type: user_model.UserTypeOrganization,
}
team := &organization.Team{
ID: 11,
OrgID: 26,
}
re := &repo_model.Repository{
ID: 43,
Owner: org,
OwnerID: 26,
}
repo := &context.Repository{
Owner: &user_model.User{
ID: 26,
LowerName: "org26",
RepoAdminChangeTeamAccess: false,
},
Repository: re,
}
ctx.Repo = repo
ctx, _ := contexttest.MockContext(t, repo.Link()+"/settings/collaboration")
ctx.Req.Form.Set("team", targetTeam.Name)
ctx.Repo = repoContext
ctx.Doer = doer
AddTeamPost(ctx)
assert.False(t, repo_service.HasRepository(t.Context(), team, re.ID))
assert.False(t, repo_service.HasRepository(t.Context(), targetTeam, repo.ID))
assert.Equal(t, http.StatusSeeOther, ctx.Resp.WrittenStatus())
assert.NotEmpty(t, ctx.Flash.ErrorMsg)
}