Backport #26182 by @Zettat123 Fix #25934 Add `ignoreGlobal` parameter to `reqUnitAccess` and only check global disabled units when `ignoreGlobal` is true. So the org-level projects and user-level projects won't be affected by global disabled `repo.projects` unit. Co-authored-by: Zettat123 <zettat123@gmail.com>tags/v1.20.3
@@ -280,3 +280,9 @@ | |||
team_id: 20 | |||
type: 9 # package | |||
access_mode: 2 | |||
- | |||
id: 48 | |||
team_id: 2 | |||
type: 8 | |||
access_mode: 2 |
@@ -262,9 +262,10 @@ func registerRoutes(m *web.Route) { | |||
} | |||
} | |||
reqUnitAccess := func(unitType unit.Type, accessMode perm.AccessMode) func(ctx *context.Context) { | |||
reqUnitAccess := func(unitType unit.Type, accessMode perm.AccessMode, ignoreGlobal bool) func(ctx *context.Context) { | |||
return func(ctx *context.Context) { | |||
if unitType.UnitGlobalDisabled() { | |||
// only check global disabled units when ignoreGlobal is false | |||
if !ignoreGlobal && unitType.UnitGlobalDisabled() { | |||
ctx.NotFound(unitType.String(), nil) | |||
return | |||
} | |||
@@ -828,7 +829,7 @@ func registerRoutes(m *web.Route) { | |||
m.Group("", func() { | |||
m.Get("", org.Projects) | |||
m.Get("/{id}", org.ViewProject) | |||
}, reqUnitAccess(unit.TypeProjects, perm.AccessModeRead)) | |||
}, reqUnitAccess(unit.TypeProjects, perm.AccessModeRead, true)) | |||
m.Group("", func() { //nolint:dupl | |||
m.Get("/new", org.RenderNewProject) | |||
m.Post("/new", web.Bind(forms.CreateProjectForm{}), org.NewProjectPost) | |||
@@ -849,17 +850,17 @@ func registerRoutes(m *web.Route) { | |||
m.Post("/move", org.MoveIssues) | |||
}) | |||
}) | |||
}, reqSignIn, reqUnitAccess(unit.TypeProjects, perm.AccessModeWrite), func(ctx *context.Context) { | |||
}, reqSignIn, reqUnitAccess(unit.TypeProjects, perm.AccessModeWrite, true), func(ctx *context.Context) { | |||
if ctx.ContextUser.IsIndividual() && ctx.ContextUser.ID != ctx.Doer.ID { | |||
ctx.NotFound("NewProject", nil) | |||
return | |||
} | |||
}) | |||
}, repo.MustEnableProjects) | |||
}) | |||
m.Group("", func() { | |||
m.Get("/code", user.CodeSearch) | |||
}, reqUnitAccess(unit.TypeCode, perm.AccessModeRead)) | |||
}, reqUnitAccess(unit.TypeCode, perm.AccessModeRead, false)) | |||
}, ignSignIn, context_service.UserAssignmentWeb(), context.OrgAssignment()) // for "/{username}/-" (packages, projects, code) | |||
// ***** Release Attachment Download without Signin |
@@ -0,0 +1,61 @@ | |||
// Copyright 2023 The Gitea Authors. All rights reserved. | |||
// SPDX-License-Identifier: MIT | |||
package integration | |||
import ( | |||
"net/http" | |||
"testing" | |||
unit_model "code.gitea.io/gitea/models/unit" | |||
"code.gitea.io/gitea/tests" | |||
) | |||
func TestOrgProjectAccess(t *testing.T) { | |||
defer tests.PrepareTestEnv(t)() | |||
// disable repo project unit | |||
unit_model.DisabledRepoUnits = []unit_model.Type{unit_model.TypeProjects} | |||
// repo project, 404 | |||
req := NewRequest(t, "GET", "/user2/repo1/projects") | |||
MakeRequest(t, req, http.StatusNotFound) | |||
// user project, 200 | |||
req = NewRequest(t, "GET", "/user2/-/projects") | |||
MakeRequest(t, req, http.StatusOK) | |||
// org project, 200 | |||
req = NewRequest(t, "GET", "/user3/-/projects") | |||
MakeRequest(t, req, http.StatusOK) | |||
// change the org's visibility to private | |||
session := loginUser(t, "user2") | |||
req = NewRequestWithValues(t, "POST", "/org/user3/settings", map[string]string{ | |||
"_csrf": GetCSRF(t, session, "/user3/-/projects"), | |||
"name": "user3", | |||
"visibility": "2", | |||
}) | |||
session.MakeRequest(t, req, http.StatusSeeOther) | |||
// user4 can still access the org's project because its team(team1) has the permission | |||
session = loginUser(t, "user4") | |||
req = NewRequest(t, "GET", "/user3/-/projects") | |||
session.MakeRequest(t, req, http.StatusOK) | |||
// disable team1's project unit | |||
session = loginUser(t, "user2") | |||
req = NewRequestWithValues(t, "POST", "/org/user3/teams/team1/edit", map[string]string{ | |||
"_csrf": GetCSRF(t, session, "/user3/-/projects"), | |||
"team_name": "team1", | |||
"repo_access": "specific", | |||
"permission": "read", | |||
"unit_8": "0", | |||
}) | |||
session.MakeRequest(t, req, http.StatusSeeOther) | |||
// user4 can no longer access the org's project | |||
session = loginUser(t, "user4") | |||
req = NewRequest(t, "GET", "/user3/-/projects") | |||
session.MakeRequest(t, req, http.StatusNotFound) | |||
} |