mirror of
https://github.com/go-gitea/gitea.git
synced 2026-09-23 13:13:38 +09:00
Merge remote-tracking branch 'origin/main' into various-sec-fixes
# Conflicts: # routers/api/v1/api.go
This commit is contained in:
+93
-32
@@ -88,6 +88,7 @@ import (
|
||||
"gitea.dev/routers/api/v1/packages"
|
||||
"gitea.dev/routers/api/v1/repo"
|
||||
"gitea.dev/routers/api/v1/settings"
|
||||
"gitea.dev/routers/api/v1/token"
|
||||
"gitea.dev/routers/api/v1/user"
|
||||
"gitea.dev/routers/common"
|
||||
"gitea.dev/services/actions"
|
||||
@@ -519,41 +520,79 @@ func reqOrgVisible() func(ctx *context.APIContext) {
|
||||
}
|
||||
}
|
||||
|
||||
// reqTeamMembership user should be an team member, or a site admin
|
||||
func reqTeamMembership() func(ctx *context.APIContext) {
|
||||
func teamAccessPrivileged(ctx *context.APIContext) (orgID int64, privileged, ok bool) {
|
||||
if ctx.IsUserSiteAdmin() {
|
||||
return 0, true, true
|
||||
}
|
||||
if ctx.Org.Team == nil {
|
||||
setting.PanicInDevOrTesting("teamAccess: unprepared context")
|
||||
ctx.APIErrorInternal(errors.New("teamAccess: unprepared context"))
|
||||
return 0, false, false
|
||||
}
|
||||
|
||||
orgID = ctx.Org.Team.OrgID
|
||||
isOwner, err := organization.IsOrganizationOwner(ctx, orgID, ctx.Doer.ID)
|
||||
if err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return 0, false, false
|
||||
} else if isOwner {
|
||||
return orgID, true, true
|
||||
}
|
||||
|
||||
isTeamMember, err := organization.IsTeamMember(ctx, orgID, ctx.Org.Team.ID, ctx.Doer.ID)
|
||||
if err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return 0, false, false
|
||||
}
|
||||
return orgID, isTeamMember, true
|
||||
}
|
||||
|
||||
func denyNonTeamMember(ctx *context.APIContext, orgID int64) {
|
||||
isOrgMember, err := organization.IsOrganizationMember(ctx, orgID, ctx.Doer.ID)
|
||||
if err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
} else if isOrgMember {
|
||||
ctx.APIError(http.StatusForbidden, "Must be a team member")
|
||||
} else {
|
||||
ctx.APIErrorNotFound()
|
||||
}
|
||||
}
|
||||
|
||||
// reqTeamReadAccess allows callers who can list the team to read its metadata.
|
||||
// Non-members are admitted by the team's visibility tier and parent org visibility.
|
||||
// Not sufficient for mutations — use reqOrgOwnership() or reqTeamMembership() for those.
|
||||
func reqTeamReadAccess() func(ctx *context.APIContext) {
|
||||
return func(ctx *context.APIContext) {
|
||||
if ctx.IsUserSiteAdmin() {
|
||||
orgID, privileged, ok := teamAccessPrivileged(ctx)
|
||||
if !ok || privileged {
|
||||
return
|
||||
}
|
||||
if ctx.Org.Team == nil {
|
||||
setting.PanicInDevOrTesting("reqTeamMembership: unprepared context")
|
||||
ctx.APIErrorInternal(errors.New("reqTeamMembership: unprepared context"))
|
||||
if ctx.Org.Organization == nil {
|
||||
setting.PanicInDevOrTesting("reqTeamReadAccess: organization not loaded")
|
||||
ctx.APIErrorInternal(errors.New("reqTeamReadAccess: organization not loaded"))
|
||||
return
|
||||
}
|
||||
|
||||
orgID := ctx.Org.Team.OrgID
|
||||
isOwner, err := organization.IsOrganizationOwner(ctx, orgID, ctx.Doer.ID)
|
||||
visible, err := ctx.Org.Team.CanNonMemberReadMeta(ctx, ctx.Org.Organization.AsUser(), ctx.Doer)
|
||||
if err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return
|
||||
} else if isOwner {
|
||||
return
|
||||
}
|
||||
if !visible {
|
||||
// Not admitted by visibility: 403 for org members, 404 otherwise.
|
||||
denyNonTeamMember(ctx, orgID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if isTeamMember, err := organization.IsTeamMember(ctx, orgID, ctx.Org.Team.ID, ctx.Doer.ID); err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return
|
||||
} else if !isTeamMember {
|
||||
isOrgMember, err := organization.IsOrganizationMember(ctx, orgID, ctx.Doer.ID)
|
||||
if err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
} else if isOrgMember {
|
||||
ctx.APIError(http.StatusForbidden, "Must be a team member")
|
||||
} else {
|
||||
ctx.APIErrorNotFound()
|
||||
}
|
||||
// reqTeamMembership user should be a team member, or a site admin
|
||||
func reqTeamMembership() func(ctx *context.APIContext) {
|
||||
return func(ctx *context.APIContext) {
|
||||
orgID, privileged, ok := teamAccessPrivileged(ctx)
|
||||
if !ok || privileged {
|
||||
return
|
||||
}
|
||||
denyNonTeamMember(ctx, orgID)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -663,6 +702,17 @@ func orgAssignment(args ...bool) func(ctx *context.APIContext) {
|
||||
}
|
||||
return
|
||||
}
|
||||
if ctx.Org.Organization == nil {
|
||||
ctx.Org.Organization, err = organization.GetOrgByID(ctx, ctx.Org.Team.OrgID)
|
||||
if err != nil {
|
||||
if organization.IsErrOrgNotExist(err) {
|
||||
ctx.APIErrorNotFound()
|
||||
} else {
|
||||
ctx.APIErrorInternal(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -991,6 +1041,11 @@ func Routes() *web.Router {
|
||||
})
|
||||
})
|
||||
|
||||
// Token introspection and deletion endpoint
|
||||
m.Combo("/token").
|
||||
Get(reqToken(), token.GetCurrentToken).
|
||||
Delete(reqToken(), token.DeleteCurrentToken)
|
||||
|
||||
// Notifications (requires 'notifications' scope)
|
||||
// The notifications API is not available for public-only tokens because a user's notifications mix
|
||||
// public and private repository events in the same mailbox.
|
||||
@@ -1712,25 +1767,31 @@ func Routes() *web.Router {
|
||||
}, reqToken(), reqOrgOwnership())
|
||||
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), orgAssignment(true), checkTokenPublicOnly())
|
||||
m.Group("/teams/{teamid}", func() {
|
||||
m.Combo("").Get(reqToken(), org.GetTeam).
|
||||
Patch(reqToken(), reqOrgOwnership(), bind(api.EditTeamOption{}), org.EditTeam).
|
||||
m.Combo("").Patch(reqToken(), reqOrgOwnership(), bind(api.EditTeamOption{}), org.EditTeam).
|
||||
Delete(reqToken(), reqOrgOwnership(), org.DeleteTeam)
|
||||
m.Group("", func() {
|
||||
m.Get("", org.GetTeam)
|
||||
m.Group("/members", func() {
|
||||
m.Get("", reqOrgMembership(), org.GetTeamMembers)
|
||||
m.Combo("/{username}").Get(reqOrgMembership(), org.GetTeamMember)
|
||||
})
|
||||
m.Group("/repos", func() {
|
||||
m.Get("", org.GetTeamRepos)
|
||||
m.Combo("/{org}/{reponame}").Get(org.GetTeamRepo)
|
||||
})
|
||||
m.Get("/activities/feeds", org.ListTeamActivityFeeds)
|
||||
}, reqTeamReadAccess())
|
||||
m.Group("/members", func() {
|
||||
m.Get("", reqToken(), org.GetTeamMembers)
|
||||
m.Combo("/{username}").
|
||||
Get(reqToken(), org.GetTeamMember).
|
||||
Put(reqToken(), reqOrgOwnership(), org.AddTeamMember).
|
||||
Delete(reqToken(), reqOrgOwnership(), org.RemoveTeamMember)
|
||||
})
|
||||
m.Group("/repos", func() {
|
||||
m.Get("", reqToken(), org.GetTeamRepos)
|
||||
m.Combo("/{org}/{reponame}").
|
||||
Put(reqToken(), org.AddTeamRepository).
|
||||
Delete(reqToken(), org.RemoveTeamRepository).
|
||||
Get(reqToken(), org.GetTeamRepo)
|
||||
Put(reqToken(), reqTeamMembership(), org.AddTeamRepository).
|
||||
Delete(reqToken(), reqTeamMembership(), org.RemoveTeamRepository)
|
||||
})
|
||||
m.Get("/activities/feeds", org.ListTeamActivityFeeds)
|
||||
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), orgAssignment(false, true), reqToken(), reqTeamMembership(), checkTokenPublicOnly())
|
||||
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), orgAssignment(false, true), reqToken(), checkTokenPublicOnly())
|
||||
|
||||
m.Group("/admin", func() {
|
||||
m.Group("/cron", func() {
|
||||
|
||||
Reference in New Issue
Block a user