mirror of
https://github.com/go-gitea/gitea.git
synced 2026-09-08 14:03:24 +09:00
fix: grant limited-org unit read access to authenticated non-members (#38871)
Fixes #38870 Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
@@ -140,10 +140,6 @@ func (org *Organization) GetMembers(ctx context.Context, doer *user_model.User)
|
|||||||
|
|
||||||
// HasMemberWithUserID returns true if user with userID is part of the u organisation.
|
// HasMemberWithUserID returns true if user with userID is part of the u organisation.
|
||||||
func (org *Organization) HasMemberWithUserID(ctx context.Context, userID int64) bool {
|
func (org *Organization) HasMemberWithUserID(ctx context.Context, userID int64) bool {
|
||||||
return org.hasMemberWithUserID(ctx, userID)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (org *Organization) hasMemberWithUserID(ctx context.Context, userID int64) bool {
|
|
||||||
isMember, err := IsOrganizationMember(ctx, org.ID, userID)
|
isMember, err := IsOrganizationMember(ctx, org.ID, userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("IsOrganizationMember: %v", err)
|
log.Error("IsOrganizationMember: %v", err)
|
||||||
@@ -303,7 +299,7 @@ func (org *Organization) AnyRepoUnitPermission(ctx context.Context, doer *user_m
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if org.Visibility.IsPublic() {
|
if ownerVisibilitySatisfiesDoer(org.AsUser(), doer) {
|
||||||
return perm.AccessModeRead
|
return perm.AccessModeRead
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -445,8 +441,7 @@ func GetUsersWhoCanCreateOrgRepo(ctx context.Context, orgID int64) (map[int64]*u
|
|||||||
And("team_user.org_id = ?", orgID).Find(&users)
|
And("team_user.org_id = ?", orgID).Find(&users)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasOrgOrUserVisible tells if the given user can see the given org or user
|
func ownerVisibilitySatisfiesDoer(orgOrUser, user *user_model.User) bool {
|
||||||
func HasOrgOrUserVisible(ctx context.Context, orgOrUser, user *user_model.User) bool {
|
|
||||||
// If user is nil, it's an anonymous user/request.
|
// If user is nil, it's an anonymous user/request.
|
||||||
// The Ghost user is handled like an anonymous user.
|
// The Ghost user is handled like an anonymous user.
|
||||||
if user == nil || user.IsGhost() {
|
if user == nil || user.IsGhost() {
|
||||||
@@ -461,18 +456,17 @@ func HasOrgOrUserVisible(ctx context.Context, orgOrUser, user *user_model.User)
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (orgOrUser.Visibility == structs.VisibleTypePrivate || user.IsRestricted) && !OrgFromUser(orgOrUser).hasMemberWithUserID(ctx, user.ID) {
|
return orgOrUser.Visibility != structs.VisibleTypePrivate && !user.IsRestricted
|
||||||
return false
|
}
|
||||||
}
|
|
||||||
return true
|
// HasOrgOrUserVisible tells if the given user can see the given org or user
|
||||||
|
func HasOrgOrUserVisible(ctx context.Context, owner, doer *user_model.User) bool {
|
||||||
|
return ownerVisibilitySatisfiesDoer(owner, doer) ||
|
||||||
|
(doer != nil && OrgFromUser(owner).HasMemberWithUserID(ctx, doer.ID))
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasOrgsVisible tells if the given user can see at least one of the orgs provided
|
// HasOrgsVisible tells if the given user can see at least one of the orgs provided
|
||||||
func HasOrgsVisible(ctx context.Context, orgs []*Organization, user *user_model.User) bool {
|
func HasOrgsVisible(ctx context.Context, orgs []*Organization, user *user_model.User) bool {
|
||||||
if len(orgs) == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, org := range orgs {
|
for _, org := range orgs {
|
||||||
if HasOrgOrUserVisible(ctx, org.AsUser(), user) {
|
if HasOrgOrUserVisible(ctx, org.AsUser(), user) {
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -10,7 +10,9 @@ import (
|
|||||||
|
|
||||||
"gitea.dev/models/db"
|
"gitea.dev/models/db"
|
||||||
"gitea.dev/models/organization"
|
"gitea.dev/models/organization"
|
||||||
|
"gitea.dev/models/perm"
|
||||||
repo_model "gitea.dev/models/repo"
|
repo_model "gitea.dev/models/repo"
|
||||||
|
"gitea.dev/models/unit"
|
||||||
"gitea.dev/models/unittest"
|
"gitea.dev/models/unittest"
|
||||||
user_model "gitea.dev/models/user"
|
user_model "gitea.dev/models/user"
|
||||||
"gitea.dev/modules/setting"
|
"gitea.dev/modules/setting"
|
||||||
@@ -626,3 +628,10 @@ func TestCreateOrganization4(t *testing.T) {
|
|||||||
assert.True(t, db.IsErrNameReserved(err))
|
assert.True(t, db.IsErrNameReserved(err))
|
||||||
unittest.CheckConsistencyFor(t, &organization.Organization{}, &organization.Team{})
|
unittest.CheckConsistencyFor(t, &organization.Organization{}, &organization.Team{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestOrAnyRepoUnitPermission(t *testing.T) {
|
||||||
|
defer test.MockVariableValue(&setting.Service.RequireSignInViewStrict, true)()
|
||||||
|
org := organization.Organization{Visibility: structs.VisibleTypeLimited}
|
||||||
|
assert.Equal(t, perm.AccessModeNone, org.AnyRepoUnitPermission(t.Context(), nil, unit.TypeWiki))
|
||||||
|
assert.Equal(t, perm.AccessModeRead, org.AnyRepoUnitPermission(t.Context(), &user_model.User{}, unit.TypeWiki))
|
||||||
|
}
|
||||||
|
|||||||
+6
-12
@@ -441,19 +441,13 @@ func registerWebRoutes(m *web.Router, webAuth *AuthMiddleware) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
reqUnitAccess := func(unitType unit.Type, accessMode perm.AccessMode, ignoreGlobal bool) func(ctx *context.Context) {
|
reqAnyRepoUnitAccess := func(unitType unit.Type, accessMode perm.AccessMode, ignoreGlobal bool) func(ctx *context.Context) {
|
||||||
return func(ctx *context.Context) {
|
return func(ctx *context.Context) {
|
||||||
// only check global disabled units when ignoreGlobal is false
|
// only check global disabled units when ignoreGlobal is false
|
||||||
if !ignoreGlobal && unitType.UnitGlobalDisabled() {
|
if !ignoreGlobal && unitType.UnitGlobalDisabled() {
|
||||||
ctx.NotFound(nil)
|
ctx.NotFound(nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.ContextUser == nil {
|
|
||||||
ctx.NotFound(nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if ctx.ContextUser.IsOrganization() {
|
if ctx.ContextUser.IsOrganization() {
|
||||||
if ctx.Org.Organization.AnyRepoUnitPermission(ctx, ctx.Doer, unitType) < accessMode {
|
if ctx.Org.Organization.AnyRepoUnitPermission(ctx, ctx.Doer, unitType) < accessMode {
|
||||||
ctx.NotFound(nil)
|
ctx.NotFound(nil)
|
||||||
@@ -1125,7 +1119,7 @@ func registerWebRoutes(m *web.Router, webAuth *AuthMiddleware) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// at the moment, only editing "owner-level projects" need to "mention", maybe in the future we can relax the permission check
|
// at the moment, only editing "owner-level projects" need to "mention", maybe in the future we can relax the permission check
|
||||||
m.Get("/mentions-in-owner", reqUnitAccess(unit.TypeProjects, perm.AccessModeWrite, true), org.GetMentionsInOwner)
|
m.Get("/mentions-in-owner", reqAnyRepoUnitAccess(unit.TypeProjects, perm.AccessModeWrite, true), org.GetMentionsInOwner)
|
||||||
|
|
||||||
m.Get("/repositories", org.Repositories)
|
m.Get("/repositories", org.Repositories)
|
||||||
m.Get("/heatmap", user.DashboardHeatmap)
|
m.Get("/heatmap", user.DashboardHeatmap)
|
||||||
@@ -1134,7 +1128,7 @@ func registerWebRoutes(m *web.Router, webAuth *AuthMiddleware) {
|
|||||||
m.Group("", func() {
|
m.Group("", func() {
|
||||||
m.Get("", org.Projects)
|
m.Get("", org.Projects)
|
||||||
m.Get("/{id}", org.ViewProject)
|
m.Get("/{id}", org.ViewProject)
|
||||||
}, reqUnitAccess(unit.TypeProjects, perm.AccessModeRead, true))
|
}, reqAnyRepoUnitAccess(unit.TypeProjects, perm.AccessModeRead, true))
|
||||||
m.Group("", func() {
|
m.Group("", func() {
|
||||||
m.Get("/new", org.RenderNewProject)
|
m.Get("/new", org.RenderNewProject)
|
||||||
m.Post("/new", web.Bind[*forms.CreateProjectForm](), org.NewProjectPost)
|
m.Post("/new", web.Bind[*forms.CreateProjectForm](), org.NewProjectPost)
|
||||||
@@ -1147,17 +1141,17 @@ func registerWebRoutes(m *web.Router, webAuth *AuthMiddleware) {
|
|||||||
|
|
||||||
addProjectBoardRoutes(m)
|
addProjectBoardRoutes(m)
|
||||||
})
|
})
|
||||||
}, reqSignIn, reqUnitAccess(unit.TypeProjects, perm.AccessModeWrite, true), func(ctx *context.Context) {
|
}, reqSignIn, reqAnyRepoUnitAccess(unit.TypeProjects, perm.AccessModeWrite, true), func(ctx *context.Context) {
|
||||||
if ctx.ContextUser.IsIndividual() && ctx.ContextUser.ID != ctx.Doer.ID {
|
if ctx.ContextUser.IsIndividual() && ctx.ContextUser.ID != ctx.Doer.ID {
|
||||||
ctx.NotFound(nil)
|
ctx.NotFound(nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, reqUnitAccess(unit.TypeProjects, perm.AccessModeRead, true), individualPermsChecker)
|
}, reqAnyRepoUnitAccess(unit.TypeProjects, perm.AccessModeRead, true), individualPermsChecker)
|
||||||
|
|
||||||
m.Group("", func() {
|
m.Group("", func() {
|
||||||
m.Get("/code", user.CodeSearch)
|
m.Get("/code", user.CodeSearch)
|
||||||
}, reqUnitAccess(unit.TypeCode, perm.AccessModeRead, false), individualPermsChecker)
|
}, reqAnyRepoUnitAccess(unit.TypeCode, perm.AccessModeRead, false), individualPermsChecker)
|
||||||
}, optSignIn, context.UserAssignmentWeb(), context.OrgAssignment(context.OrgAssignmentOptions{}))
|
}, optSignIn, context.UserAssignmentWeb(), context.OrgAssignment(context.OrgAssignmentOptions{}))
|
||||||
// end "/{username}/-": packages, projects, code
|
// end "/{username}/-": packages, projects, code
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user