Skip to content

Commit c904556

Browse files
authored
Merge branch 'main' into minioIamDiscoverySupport
2 parents 1967ec2 + 407b6e6 commit c904556

File tree

4 files changed

+105
-3
lines changed

4 files changed

+105
-3
lines changed

models/fixtures/action_task.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,22 @@
1+
-
2+
id: 46
3+
attempt: 3
4+
runner_id: 1
5+
status: 3 # 3 is the status code for "cancelled"
6+
started: 1683636528
7+
stopped: 1683636626
8+
repo_id: 4
9+
owner_id: 1
10+
commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
11+
is_fork_pull_request: 0
12+
token_hash: 6d8ef48297195edcc8e22c70b3020eaa06c52976db67d39b4260c64a69a2cc1508825121b7b8394e48e00b1bf8718b2aaaaa
13+
token_salt: eeeeeeee
14+
token_last_eight: eeeeeeee
15+
log_filename: artifact-test2/2f/47.log
16+
log_in_storage: 1
17+
log_length: 707
18+
log_size: 90179
19+
log_expired: 0
120
-
221
id: 47
322
job_id: 192

services/actions/auth.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,12 @@ func ParseAuthorizationToken(req *http.Request) (int64, error) {
8383
return 0, fmt.Errorf("split token failed")
8484
}
8585

86-
token, err := jwt.ParseWithClaims(parts[1], &actionsClaims{}, func(t *jwt.Token) (any, error) {
86+
return TokenToTaskID(parts[1])
87+
}
88+
89+
// TokenToTaskID returns the TaskID associated with the provided JWT token
90+
func TokenToTaskID(token string) (int64, error) {
91+
parsedToken, err := jwt.ParseWithClaims(token, &actionsClaims{}, func(t *jwt.Token) (any, error) {
8792
if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
8893
return nil, fmt.Errorf("unexpected signing method: %v", t.Header["alg"])
8994
}
@@ -93,8 +98,8 @@ func ParseAuthorizationToken(req *http.Request) (int64, error) {
9398
return 0, err
9499
}
95100

96-
c, ok := token.Claims.(*actionsClaims)
97-
if !token.Valid || !ok {
101+
c, ok := parsedToken.Claims.(*actionsClaims)
102+
if !parsedToken.Valid || !ok {
98103
return 0, fmt.Errorf("invalid token claim")
99104
}
100105

services/auth/oauth2.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"code.gitea.io/gitea/modules/setting"
1818
"code.gitea.io/gitea/modules/timeutil"
1919
"code.gitea.io/gitea/modules/web/middleware"
20+
"code.gitea.io/gitea/services/actions"
2021
"code.gitea.io/gitea/services/oauth2_provider"
2122
)
2223

@@ -54,6 +55,18 @@ func CheckOAuthAccessToken(ctx context.Context, accessToken string) int64 {
5455
return grant.UserID
5556
}
5657

58+
// CheckTaskIsRunning verifies that the TaskID corresponds to a running task
59+
func CheckTaskIsRunning(ctx context.Context, taskID int64) bool {
60+
// Verify the task exists
61+
task, err := actions_model.GetTaskByID(ctx, taskID)
62+
if err != nil {
63+
return false
64+
}
65+
66+
// Verify that it's running
67+
return task.Status == actions_model.StatusRunning
68+
}
69+
5770
// OAuth2 implements the Auth interface and authenticates requests
5871
// (API requests only) by looking for an OAuth token in query parameters or the
5972
// "Authorization" header.
@@ -97,6 +110,16 @@ func parseToken(req *http.Request) (string, bool) {
97110
func (o *OAuth2) userIDFromToken(ctx context.Context, tokenSHA string, store DataStore) int64 {
98111
// Let's see if token is valid.
99112
if strings.Contains(tokenSHA, ".") {
113+
// First attempt to decode an actions JWT, returning the actions user
114+
if taskID, err := actions.TokenToTaskID(tokenSHA); err == nil {
115+
if CheckTaskIsRunning(ctx, taskID) {
116+
store.GetData()["IsActionsToken"] = true
117+
store.GetData()["ActionsTaskID"] = taskID
118+
return user_model.ActionsUserID
119+
}
120+
}
121+
122+
// Otherwise, check if this is an OAuth access token
100123
uid := CheckOAuthAccessToken(ctx, tokenSHA)
101124
if uid != 0 {
102125
store.GetData()["IsApiToken"] = true

services/auth/oauth2_test.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright 2024 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package auth
5+
6+
import (
7+
"context"
8+
"testing"
9+
10+
"code.gitea.io/gitea/models/unittest"
11+
user_model "code.gitea.io/gitea/models/user"
12+
"code.gitea.io/gitea/modules/web/middleware"
13+
"code.gitea.io/gitea/services/actions"
14+
15+
"github.com/stretchr/testify/assert"
16+
)
17+
18+
func TestUserIDFromToken(t *testing.T) {
19+
assert.NoError(t, unittest.PrepareTestDatabase())
20+
21+
t.Run("Actions JWT", func(t *testing.T) {
22+
const RunningTaskID = 47
23+
token, err := actions.CreateAuthorizationToken(RunningTaskID, 1, 2)
24+
assert.NoError(t, err)
25+
26+
ds := make(middleware.ContextData)
27+
28+
o := OAuth2{}
29+
uid := o.userIDFromToken(context.Background(), token, ds)
30+
assert.Equal(t, int64(user_model.ActionsUserID), uid)
31+
assert.Equal(t, ds["IsActionsToken"], true)
32+
assert.Equal(t, ds["ActionsTaskID"], int64(RunningTaskID))
33+
})
34+
}
35+
36+
func TestCheckTaskIsRunning(t *testing.T) {
37+
assert.NoError(t, unittest.PrepareTestDatabase())
38+
39+
cases := map[string]struct {
40+
TaskID int64
41+
Expected bool
42+
}{
43+
"Running": {TaskID: 47, Expected: true},
44+
"Missing": {TaskID: 1, Expected: false},
45+
"Cancelled": {TaskID: 46, Expected: false},
46+
}
47+
48+
for name := range cases {
49+
c := cases[name]
50+
t.Run(name, func(t *testing.T) {
51+
actual := CheckTaskIsRunning(context.Background(), c.TaskID)
52+
assert.Equal(t, c.Expected, actual)
53+
})
54+
}
55+
}

0 commit comments

Comments
 (0)