Skip to content

Commit 060d4ed

Browse files
lunnyGiteaBot
authored andcommitted
Fix system admin cannot fork or get private fork with API (go-gitea#33401)
Fix go-gitea#33368
1 parent c30f4f4 commit 060d4ed

File tree

4 files changed

+47
-13
lines changed

4 files changed

+47
-13
lines changed

routers/api/v1/repo/fork.go

+9-7
Original file line numberDiff line numberDiff line change
@@ -132,13 +132,15 @@ func CreateFork(ctx *context.APIContext) {
132132
}
133133
return
134134
}
135-
isMember, err := org.IsOrgMember(ctx, ctx.Doer.ID)
136-
if err != nil {
137-
ctx.Error(http.StatusInternalServerError, "IsOrgMember", err)
138-
return
139-
} else if !isMember {
140-
ctx.Error(http.StatusForbidden, "isMemberNot", fmt.Sprintf("User is no Member of Organisation '%s'", org.Name))
141-
return
135+
if !ctx.Doer.IsAdmin {
136+
isMember, err := org.IsOrgMember(ctx, ctx.Doer.ID)
137+
if err != nil {
138+
ctx.Error(http.StatusInternalServerError, "IsOrgMember", err)
139+
return
140+
} else if !isMember {
141+
ctx.Error(http.StatusForbidden, "isMemberNot", fmt.Sprintf("User is no Member of Organisation '%s'", org.Name))
142+
return
143+
}
142144
}
143145
forker = org.AsUser()
144146
}

services/repository/fork.go

+5-3
Original file line numberDiff line numberDiff line change
@@ -258,9 +258,11 @@ type findForksOptions struct {
258258
}
259259

260260
func (opts findForksOptions) ToConds() builder.Cond {
261-
return builder.Eq{"fork_id": opts.RepoID}.And(
262-
repo_model.AccessibleRepositoryCondition(opts.Doer, unit.TypeInvalid),
263-
)
261+
cond := builder.Eq{"fork_id": opts.RepoID}
262+
if opts.Doer != nil && opts.Doer.IsAdmin {
263+
return cond
264+
}
265+
return cond.And(repo_model.AccessibleRepositoryCondition(opts.Doer, unit.TypeInvalid))
264266
}
265267

266268
// FindForks returns all the forks of the repository

tests/integration/api_fork_test.go

+31-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
auth_model "code.gitea.io/gitea/models/auth"
1111
"code.gitea.io/gitea/models/db"
1212
org_model "code.gitea.io/gitea/models/organization"
13+
repo_model "code.gitea.io/gitea/models/repo"
1314
"code.gitea.io/gitea/models/unittest"
1415
user_model "code.gitea.io/gitea/models/user"
1516
api "code.gitea.io/gitea/modules/structs"
@@ -81,8 +82,8 @@ func TestAPIForkListLimitedAndPrivateRepos(t *testing.T) {
8182
var forks []*api.Repository
8283
DecodeJSON(t, resp, &forks)
8384

84-
assert.Len(t, forks, 1)
85-
assert.EqualValues(t, "1", resp.Header().Get("X-Total-Count"))
85+
assert.Len(t, forks, 2)
86+
assert.EqualValues(t, "2", resp.Header().Get("X-Total-Count"))
8687

8788
assert.NoError(t, org_service.AddTeamMember(db.DefaultContext, ownerTeam2, user1))
8889

@@ -96,3 +97,31 @@ func TestAPIForkListLimitedAndPrivateRepos(t *testing.T) {
9697
assert.EqualValues(t, "2", resp.Header().Get("X-Total-Count"))
9798
})
9899
}
100+
101+
func TestGetPrivateReposForks(t *testing.T) {
102+
defer tests.PrepareTestEnv(t)()
103+
104+
user1Sess := loginUser(t, "user1")
105+
repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) // private repository
106+
privateOrg := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 23})
107+
user1Token := getTokenForLoggedInUser(t, user1Sess, auth_model.AccessTokenScopeWriteRepository)
108+
109+
forkedRepoName := "forked-repo"
110+
// create fork from a private repository
111+
req := NewRequestWithJSON(t, "POST", "/api/v1/repos/"+repo2.FullName()+"/forks", &api.CreateForkOption{
112+
Organization: &privateOrg.Name,
113+
Name: &forkedRepoName,
114+
}).AddTokenAuth(user1Token)
115+
MakeRequest(t, req, http.StatusAccepted)
116+
117+
// test get a private fork without clear permissions
118+
req = NewRequest(t, "GET", "/api/v1/repos/"+repo2.FullName()+"/forks").AddTokenAuth(user1Token)
119+
resp := MakeRequest(t, req, http.StatusOK)
120+
121+
forks := []*api.Repository{}
122+
DecodeJSON(t, resp, &forks)
123+
assert.Len(t, forks, 1)
124+
assert.EqualValues(t, "1", resp.Header().Get("X-Total-Count"))
125+
assert.EqualValues(t, "forked-repo", forks[0].Name)
126+
assert.EqualValues(t, privateOrg.Name, forks[0].Owner.UserName)
127+
}

tests/integration/repo_fork_test.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,8 @@ func TestForkListLimitedAndPrivateRepos(t *testing.T) {
118118
req := NewRequest(t, "GET", "/user2/repo1/forks")
119119
resp := user1Sess.MakeRequest(t, req, http.StatusOK)
120120
htmlDoc := NewHTMLParser(t, resp.Body)
121-
assert.EqualValues(t, 1, htmlDoc.Find(forkItemSelector).Length())
121+
// since user1 is an admin, he can get both of the forked repositories
122+
assert.EqualValues(t, 2, htmlDoc.Find(forkItemSelector).Length())
122123

123124
assert.NoError(t, org_service.AddTeamMember(db.DefaultContext, ownerTeam2, user1))
124125
resp = user1Sess.MakeRequest(t, req, http.StatusOK)

0 commit comments

Comments
 (0)