From 7884e2f141b847307d076d37d43c23e246071393 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sat, 30 May 2026 12:26:24 -0500 Subject: [PATCH] =?UTF-8?q?feat(permissions):=20section-based=20visibility?= =?UTF-8?q?=20=E2=80=94=20public=20units=20on=20private=20repos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix three gaps that prevented per-unit public access from working on private repositories: 1. Git HTTP handler (githttp.go): allow anonymous git pull for private repos when the target unit (code or wiki) has AnonymousAccessMode set to read. Previously only checked repo.IsPrivate. 2. Permission engine (repo_permission.go): call finalProcessRepoUnitPermission for anonymous users on private repos so that unit-level anonymous access modes are populated. Previously returned early with AccessModeNone, skipping anonymous mode setup. 3. Search/explore (repo_list.go): include private repos that have at least one unit with anonymous_access_mode > 0 in search results, so anonymous users can discover repos with public sections. The existing settings UI at /settings/public_access already allows configuring per-unit visibility. The home page redirect to the first readable unit (e.g. wiki) also already works via checkHomeCodeViewable. Closes #238 Co-Authored-By: Claude Opus 4.6 (1M context) --- models/perm/access/repo_permission.go | 3 +++ models/repo/repo_list.go | 8 ++++++++ routers/web/repo/githttp.go | 10 +++++++++- 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/models/perm/access/repo_permission.go b/models/perm/access/repo_permission.go index c14d5e9341..3168b90a5f 100644 --- a/models/perm/access/repo_permission.go +++ b/models/perm/access/repo_permission.go @@ -405,8 +405,11 @@ func GetIndividualUserRepoPermission(ctx context.Context, repo *repo_model.Repos perm.units = repo.Units // anonymous user visit private repo. + // Still process unit-level anonymous access so that units with + // AnonymousAccessMode (e.g. public wiki on a private repo) are visible. if user == nil && repo.IsPrivate { perm.AccessMode = perm_model.AccessModeNone + finalProcessRepoUnitPermission(user, &perm) return perm, nil } diff --git a/models/repo/repo_list.go b/models/repo/repo_list.go index 3903a56b14..99c547bc17 100644 --- a/models/repo/repo_list.go +++ b/models/repo/repo_list.go @@ -673,6 +673,14 @@ func AccessibleRepositoryCondition(user *user_model.User, unitType unit.Type) bu cond = userAllPublicRepoCond(cond, orgVisibilityLimit) } + // Include private repos that have at least one unit with public anonymous access. + // This enables discovery of repos where e.g. wiki or releases are public. + cond = cond.Or(builder.In("`repository`.id", + builder.Select("repo_id").From("repo_unit").Where( + builder.Gt{"anonymous_access_mode": 0}, + ), + )) + if user != nil { // 2. Be able to see all repositories that we have unit independent access to // 3. Be able to see all repositories through team membership(s) diff --git a/routers/web/repo/githttp.go b/routers/web/repo/githttp.go index 2e2c3f3b01..403c5d89b7 100644 --- a/routers/web/repo/githttp.go +++ b/routers/web/repo/githttp.go @@ -128,7 +128,15 @@ func httpBase(ctx *context.Context, optGitService ...string) *serviceHandler { } // Only public pull don't need auth. - isPublicPull := repoExist && !repo.IsPrivate && isPull + // For private repos, also allow anonymous pull if the specific unit + // (code or wiki) has AnonymousAccessMode >= Read. + isPublicPull := repoExist && isPull && !repo.IsPrivate + if repoExist && isPull && repo.IsPrivate { + repoUnit := repo.MustGetUnit(ctx, unitType) + if repoUnit.AnonymousAccessMode >= perm.AccessModeRead { + isPublicPull = true + } + } askAuth := !isPublicPull || setting.Service.RequireSignInViewStrict // don't allow anonymous pulls if organization is not public