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
team_id: 20 | team_id: 20 | ||||
type: 9 # package | type: 9 # package | ||||
access_mode: 2 | access_mode: 2 | ||||
- | |||||
id: 48 | |||||
team_id: 2 | |||||
type: 8 | |||||
access_mode: 2 |
} | } | ||||
} | } | ||||
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) { | 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) | ctx.NotFound(unitType.String(), nil) | ||||
return | return | ||||
} | } | ||||
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)) | |||||
}, reqUnitAccess(unit.TypeProjects, perm.AccessModeRead, true)) | |||||
m.Group("", func() { //nolint:dupl | m.Group("", func() { //nolint:dupl | ||||
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) | ||||
m.Post("/move", org.MoveIssues) | 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 { | if ctx.ContextUser.IsIndividual() && ctx.ContextUser.ID != ctx.Doer.ID { | ||||
ctx.NotFound("NewProject", nil) | ctx.NotFound("NewProject", nil) | ||||
return | return | ||||
} | } | ||||
}) | }) | ||||
}, repo.MustEnableProjects) | |||||
}) | |||||
m.Group("", func() { | m.Group("", func() { | ||||
m.Get("/code", user.CodeSearch) | 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) | }, ignSignIn, context_service.UserAssignmentWeb(), context.OrgAssignment()) // for "/{username}/-" (packages, projects, code) | ||||
// ***** Release Attachment Download without Signin | // ***** Release Attachment Download without Signin |
// 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) | |||||
} |