fix(repo): honor ALLOW_FORK_INTO_SAME_OWNER for organizations (#40025)

With `[repository] ALLOW_FORK_INTO_SAME_OWNER = true` a user can fork
their own repository, but an organization still cannot fork a repository
it owns. The fork button check and the owner list on the fork page
compared the organization with the repository owner directly instead of
asking `CanUserForkBetweenOwners`, and the walk over parent repositories
dropped the owning organization whenever the setting was on.
Organizations now follow the setting the same way users already do, and
nothing changes when it is off.

Fixes https://github.com/go-gitea/gitea/issues/40021
Assisted-by: Claude Code:claude-opus-5-5
This commit is contained in:
somaz authored and GitHub committed 2026-10-30 17:28:47 +00:00
1 parent eb3bc196ec
commit 140fac63e2
4 files changed
+42 -10

No files matched your search

+1 -1
View File
@@ -35,7 +35,7 @@ func CanUserForkRepo(ctx context.Context, user *user_model.User, repo *repo_mode
return false, err
}
for _, org := range ownedOrgs {
if repo.OwnerID != org.ID && !repo_model.HasForkedRepo(ctx, org.ID, repo.ID) {
if CanUserForkBetweenOwners(repo.OwnerID, org.ID) && !repo_model.HasForkedRepo(ctx, org.ID, repo.ID) {
return true, nil
}
}
+28 -1
View File
@@ -6,6 +6,10 @@ package repository
import (
"testing"
"gitea.dev/models/db"
repo_model "gitea.dev/models/repo"
"gitea.dev/models/unittest"
user_model "gitea.dev/models/user"
"gitea.dev/modules/setting"
"gitea.dev/modules/test"
@@ -13,7 +17,7 @@ import (
)
func TestCanUserForkBetweenOwners(t *testing.T) {
defer test.MockVariableValue(&setting.Repository.AllowForkIntoSameOwner)
defer test.MockVariableValue(&setting.Repository.AllowForkIntoSameOwner)()
setting.Repository.AllowForkIntoSameOwner = true
assert.True(t, CanUserForkBetweenOwners(1, 1))
@@ -23,3 +27,26 @@ func TestCanUserForkBetweenOwners(t *testing.T) {
assert.False(t, CanUserForkBetweenOwners(1, 1))
assert.True(t, CanUserForkBetweenOwners(1, 2))
}
func TestCanUserForkRepoIntoOwningOrg(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
defer test.MockVariableValue(&setting.Repository.AllowForkIntoSameOwner)()
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
// user2's own fork leaves org3, the owner, as the only target
assert.NoError(t, db.Insert(t.Context(), &repo_model.Repository{
OwnerID: 2, Name: "repo3-fork", LowerName: "repo3-fork",
IsFork: true, ForkID: 3,
}))
setting.Repository.AllowForkIntoSameOwner = true
ok, err := CanUserForkRepo(t.Context(), user2, repo3)
assert.NoError(t, err)
assert.True(t, ok)
setting.Repository.AllowForkIntoSameOwner = false
ok, err = CanUserForkRepo(t.Context(), user2, repo3)
assert.NoError(t, err)
assert.False(t, ok)
}
+5 -8
View File
@@ -7,6 +7,7 @@ import (
"errors"
"net/http"
"net/url"
"slices"
"gitea.dev/models/db"
git_model "gitea.dev/models/git"
@@ -60,7 +61,7 @@ func getForkRepository(ctx *context.Context) *repo_model.Repository {
}
var orgs []*organization.Organization
for _, org := range ownedOrgs {
if forkRepo.OwnerID != org.ID && !repo_model.HasForkedRepo(ctx, org.ID, forkRepo.ID) {
if repository.CanUserForkBetweenOwners(forkRepo.OwnerID, org.ID) && !repo_model.HasForkedRepo(ctx, org.ID, forkRepo.ID) {
orgs = append(orgs, org)
}
}
@@ -69,14 +70,10 @@ func getForkRepository(ctx *context.Context) *repo_model.Repository {
for {
if !repository.CanUserForkBetweenOwners(ctx.Doer.ID, traverseParentRepo.OwnerID) {
canForkToUser = false
} else {
for i, org := range orgs {
if org.ID == traverseParentRepo.OwnerID {
orgs = append(orgs[:i], orgs[i+1:]...)
break
}
}
}
orgs = slices.DeleteFunc(orgs, func(org *organization.Organization) bool {
return !repository.CanUserForkBetweenOwners(org.ID, traverseParentRepo.OwnerID)
})
if !traverseParentRepo.IsFork {
break
+8
View File
@@ -13,6 +13,7 @@ import (
org_model "gitea.dev/models/organization"
"gitea.dev/models/unittest"
user_model "gitea.dev/models/user"
"gitea.dev/modules/setting"
"gitea.dev/modules/structs"
"gitea.dev/modules/test"
org_service "gitea.dev/services/org"
@@ -80,6 +81,13 @@ func TestRepoForkToOrg(t *testing.T) {
assert.False(t, exists, "Forking should not be allowed anymore")
}
func TestRepoForkIntoSameOrg(t *testing.T) {
defer tests.PrepareTestEnv(t)()
defer test.MockVariableValue(&setting.Repository.AllowForkIntoSameOwner, true)()
session := loginUser(t, "user2")
testRepoFork(t, session, "org3", "repo3", "org3", "repo3-fork", "")
}
func TestForkListLimitedAndPrivateRepos(t *testing.T) {
defer tests.PrepareTestEnv(t)()
forkItemSelector := ".fork-list .item"