Skip to content

Commit eacdea5

Browse files
committed
fix
1 parent 5b55ee6 commit eacdea5

File tree

2 files changed

+169
-8
lines changed

2 files changed

+169
-8
lines changed

routers/web/shared/actions/runners.go

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -300,8 +300,13 @@ func RunnerDeletePost(ctx *context.Context) {
300300
return
301301
}
302302

303-
runner, ok := findRunner(ctx, rCtx)
304-
if !ok {
303+
runner := findActionsRunner(ctx, rCtx)
304+
if ctx.Written() {
305+
return
306+
}
307+
308+
if !runner.Editable(rCtx.OwnerID, rCtx.RepoID) {
309+
ctx.NotFound("RunnerDeletePost", util.NewPermissionDeniedErrorf("no permission to delete this runner"))
305310
return
306311
}
307312

@@ -327,31 +332,36 @@ func RedirectToDefaultSetting(ctx *context.Context) {
327332
ctx.Redirect(ctx.Repo.RepoLink + "/settings/actions/runners")
328333
}
329334

330-
func findRunner(ctx *context.Context, rCtx *runnersCtx) (*actions_model.ActionRunner, bool) {
335+
func findActionsRunner(ctx *context.Context, rCtx *runnersCtx) *actions_model.ActionRunner {
331336
runnerID := ctx.PathParamInt64("runnerid")
332337
opts := &actions_model.FindRunnerOptions{
333338
IDs: []int64{runnerID},
334339
}
335340
switch {
336341
case rCtx.IsRepo:
337342
opts.RepoID = rCtx.RepoID
343+
if opts.RepoID == 0 {
344+
panic("repoID is 0")
345+
}
338346
case rCtx.IsOrg, rCtx.IsUser:
339347
opts.OwnerID = rCtx.OwnerID
348+
if opts.OwnerID == 0 {
349+
panic("ownerID is 0")
350+
}
340351
case rCtx.IsAdmin:
341352
// do nothing
342353
default:
343-
ctx.ServerError("findRunner", errors.New("unable to determine"))
344-
return nil, false
354+
panic("invalid actions runner context")
345355
}
346356

347357
got, err := db.Find[actions_model.ActionRunner](ctx, opts)
348358
if err != nil {
349359
ctx.ServerError("FindRunner", err)
350-
return nil, false
360+
return nil
351361
} else if len(got) == 0 {
352362
ctx.NotFound("FindRunner", errors.New("runner not found"))
353-
return nil, false
363+
return nil
354364
}
355365

356-
return got[0], true
366+
return got[0]
357367
}
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
// Copyright 2025 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package integration
5+
6+
import (
7+
"context"
8+
"fmt"
9+
"net/http"
10+
"testing"
11+
12+
actions_model "code.gitea.io/gitea/models/actions"
13+
"code.gitea.io/gitea/models/db"
14+
repo_model "code.gitea.io/gitea/models/repo"
15+
"code.gitea.io/gitea/models/unittest"
16+
user_model "code.gitea.io/gitea/models/user"
17+
"code.gitea.io/gitea/tests"
18+
19+
"github.com/stretchr/testify/assert"
20+
"github.com/stretchr/testify/require"
21+
)
22+
23+
func TestActionsRunnerModify(t *testing.T) {
24+
defer tests.PrepareTestEnv(t)()
25+
26+
ctx := context.Background()
27+
28+
require.NoError(t, db.DeleteAllRecords("action_runner"))
29+
30+
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
31+
_ = actions_model.CreateRunner(ctx, &actions_model.ActionRunner{OwnerID: user2.ID, Name: "user2-runner", TokenHash: "a", UUID: "a"})
32+
user2Runner := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRunner{OwnerID: user2.ID, Name: "user2-runner"})
33+
userWebURL := "/user/settings/actions/runners"
34+
35+
org3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3, Type: user_model.UserTypeOrganization})
36+
require.NoError(t, actions_model.CreateRunner(ctx, &actions_model.ActionRunner{OwnerID: org3.ID, Name: "org3-runner", TokenHash: "b", UUID: "b"}))
37+
org3Runner := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRunner{OwnerID: org3.ID, Name: "org3-runner"})
38+
orgWebURL := "/org/org3/settings/actions/runners"
39+
40+
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
41+
_ = actions_model.CreateRunner(ctx, &actions_model.ActionRunner{RepoID: repo1.ID, Name: "repo1-runner", TokenHash: "c", UUID: "c"})
42+
repo1Runner := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRunner{RepoID: repo1.ID, Name: "repo1-runner"})
43+
repoWebURL := "/user2/repo1/settings/actions/runners"
44+
45+
_ = actions_model.CreateRunner(ctx, &actions_model.ActionRunner{Name: "global-runner", TokenHash: "d", UUID: "d"})
46+
globalRunner := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRunner{Name: "global-runner"})
47+
adminWebURL := "/-/admin/actions/runners"
48+
49+
sessionAdmin := loginUser(t, "user1")
50+
sessionUser2 := loginUser(t, user2.Name)
51+
52+
doUpdate := func(t *testing.T, sess *TestSession, baseURL string, id int64, description string, expectedStatus int) {
53+
req := NewRequestWithValues(t, "POST", fmt.Sprintf("%s/%d", baseURL, id), map[string]string{
54+
"_csrf": GetUserCSRFToken(t, sess),
55+
"description": description,
56+
})
57+
sess.MakeRequest(t, req, expectedStatus)
58+
}
59+
60+
doDelete := func(t *testing.T, sess *TestSession, baseURL string, id int64, expectedStatus int) {
61+
req := NewRequestWithValues(t, "POST", fmt.Sprintf("%s/%d/delete", baseURL, id), map[string]string{
62+
"_csrf": GetUserCSRFToken(t, sess),
63+
})
64+
sess.MakeRequest(t, req, expectedStatus)
65+
}
66+
67+
assertDenied := func(t *testing.T, sess *TestSession, baseURL string, id int64) {
68+
doUpdate(t, sess, baseURL, id, "ChangedDescription", http.StatusNotFound)
69+
doDelete(t, sess, baseURL, id, http.StatusNotFound)
70+
v := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRunner{ID: id})
71+
assert.Empty(t, v.Description)
72+
}
73+
74+
assertSuccess := func(t *testing.T, sess *TestSession, baseURL string, id int64) {
75+
doUpdate(t, sess, baseURL, id, "ChangedDescription", http.StatusSeeOther)
76+
v := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRunner{ID: id})
77+
assert.Equal(t, "ChangedDescription", v.Description)
78+
doDelete(t, sess, baseURL, id, http.StatusOK)
79+
unittest.AssertNotExistsBean(t, &actions_model.ActionRunner{ID: id})
80+
}
81+
82+
t.Run("UpdateUserRunner", func(t *testing.T) {
83+
theRunner := user2Runner
84+
t.Run("FromOrg", func(t *testing.T) {
85+
assertDenied(t, sessionAdmin, orgWebURL, theRunner.ID)
86+
})
87+
t.Run("FromRepo", func(t *testing.T) {
88+
assertDenied(t, sessionAdmin, repoWebURL, theRunner.ID)
89+
})
90+
t.Run("FromAdmin", func(t *testing.T) {
91+
t.Skip("Admin can update any runner (not right but not too bad)")
92+
assertDenied(t, sessionAdmin, adminWebURL, theRunner.ID)
93+
})
94+
})
95+
96+
t.Run("UpdateOrgRunner", func(t *testing.T) {
97+
theRunner := org3Runner
98+
t.Run("FromRepo", func(t *testing.T) {
99+
assertDenied(t, sessionAdmin, repoWebURL, theRunner.ID)
100+
})
101+
t.Run("FromUser", func(t *testing.T) {
102+
assertDenied(t, sessionAdmin, userWebURL, theRunner.ID)
103+
})
104+
t.Run("FromAdmin", func(t *testing.T) {
105+
t.Skip("Admin can update any runner (not right but not too bad)")
106+
assertDenied(t, sessionAdmin, adminWebURL, theRunner.ID)
107+
})
108+
})
109+
110+
t.Run("UpdateRepoRunner", func(t *testing.T) {
111+
theRunner := repo1Runner
112+
t.Run("FromOrg", func(t *testing.T) {
113+
assertDenied(t, sessionAdmin, orgWebURL, theRunner.ID)
114+
})
115+
t.Run("FromUser", func(t *testing.T) {
116+
assertDenied(t, sessionAdmin, userWebURL, theRunner.ID)
117+
})
118+
t.Run("FromAdmin", func(t *testing.T) {
119+
t.Skip("Admin can update any runner (not right but not too bad)")
120+
assertDenied(t, sessionAdmin, adminWebURL, theRunner.ID)
121+
})
122+
})
123+
124+
t.Run("UpdateGlobalRunner", func(t *testing.T) {
125+
theRunner := globalRunner
126+
t.Run("FromOrg", func(t *testing.T) {
127+
assertDenied(t, sessionAdmin, orgWebURL, theRunner.ID)
128+
})
129+
t.Run("FromUser", func(t *testing.T) {
130+
assertDenied(t, sessionAdmin, userWebURL, theRunner.ID)
131+
})
132+
t.Run("FromRepo", func(t *testing.T) {
133+
assertDenied(t, sessionAdmin, repoWebURL, theRunner.ID)
134+
})
135+
})
136+
137+
t.Run("UpdateSuccess", func(t *testing.T) {
138+
t.Run("User", func(t *testing.T) {
139+
assertSuccess(t, sessionUser2, userWebURL, user2Runner.ID)
140+
})
141+
t.Run("Org", func(t *testing.T) {
142+
assertSuccess(t, sessionAdmin, orgWebURL, org3Runner.ID)
143+
})
144+
t.Run("Repo", func(t *testing.T) {
145+
assertSuccess(t, sessionUser2, repoWebURL, repo1Runner.ID)
146+
})
147+
t.Run("Admin", func(t *testing.T) {
148+
assertSuccess(t, sessionAdmin, adminWebURL, globalRunner.ID)
149+
})
150+
})
151+
}

0 commit comments

Comments
 (0)