Skip to content

Commit 4c291f1

Browse files
lunnyGiteaBot
authored andcommitted
Fix nuget/conan/container packages upload bugs (go-gitea#31967)
1 parent 244fb11 commit 4c291f1

File tree

11 files changed

+512
-90
lines changed

11 files changed

+512
-90
lines changed

models/auth/access_token_scope.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,22 @@ func (s AccessTokenScope) HasScope(scopes ...AccessTokenScope) (bool, error) {
309309
return true, nil
310310
}
311311

312+
// HasAnyScope returns true if any of the scopes is contained in the string
313+
func (s AccessTokenScope) HasAnyScope(scopes ...AccessTokenScope) (bool, error) {
314+
bitmap, err := s.parse()
315+
if err != nil {
316+
return false, err
317+
}
318+
319+
for _, s := range scopes {
320+
if has, err := bitmap.hasScope(s); has || err != nil {
321+
return has, err
322+
}
323+
}
324+
325+
return false, nil
326+
}
327+
312328
// hasScope returns true if the string has the given scope
313329
func (bitmap accessTokenScopeBitmap) hasScope(scope AccessTokenScope) (bool, error) {
314330
expectedBits, ok := allAccessTokenScopeBits[scope]

routers/api/packages/conan/auth.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,25 @@ func (a *Auth) Name() string {
2222

2323
// Verify extracts the user from the Bearer token
2424
func (a *Auth) Verify(req *http.Request, w http.ResponseWriter, store auth.DataStore, sess auth.SessionStore) (*user_model.User, error) {
25-
uid, err := packages.ParseAuthorizationToken(req)
25+
packageMeta, err := packages.ParseAuthorizationRequest(req)
2626
if err != nil {
2727
log.Trace("ParseAuthorizationToken: %v", err)
2828
return nil, err
2929
}
3030

31-
if uid == 0 {
31+
if packageMeta == nil || packageMeta.UserID == 0 {
3232
return nil, nil
3333
}
3434

35-
u, err := user_model.GetUserByID(req.Context(), uid)
35+
u, err := user_model.GetUserByID(req.Context(), packageMeta.UserID)
3636
if err != nil {
3737
log.Error("GetUserByID: %v", err)
3838
return nil, err
3939
}
40+
if packageMeta.Scope != "" {
41+
store.GetData()["IsApiToken"] = true
42+
store.GetData()["ApiTokenScope"] = packageMeta.Scope
43+
}
4044

4145
return u, nil
4246
}

routers/api/packages/conan/conan.go

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"strings"
1212
"time"
1313

14+
auth_model "code.gitea.io/gitea/models/auth"
1415
"code.gitea.io/gitea/models/db"
1516
packages_model "code.gitea.io/gitea/models/packages"
1617
conan_model "code.gitea.io/gitea/models/packages/conan"
@@ -21,6 +22,7 @@ import (
2122
conan_module "code.gitea.io/gitea/modules/packages/conan"
2223
"code.gitea.io/gitea/modules/setting"
2324
"code.gitea.io/gitea/routers/api/packages/helper"
25+
auth_service "code.gitea.io/gitea/services/auth"
2426
"code.gitea.io/gitea/services/context"
2527
notify_service "code.gitea.io/gitea/services/notify"
2628
packages_service "code.gitea.io/gitea/services/packages"
@@ -117,7 +119,20 @@ func Authenticate(ctx *context.Context) {
117119
return
118120
}
119121

120-
token, err := packages_service.CreateAuthorizationToken(ctx.Doer)
122+
packageScope := auth_service.GetAccessScope(ctx.Data)
123+
if has, err := packageScope.HasAnyScope(
124+
auth_model.AccessTokenScopeReadPackage,
125+
auth_model.AccessTokenScopeWritePackage,
126+
auth_model.AccessTokenScopeAll,
127+
); !has {
128+
if err != nil {
129+
log.Error("Error checking access scope: %v", err)
130+
}
131+
apiError(ctx, http.StatusForbidden, nil)
132+
return
133+
}
134+
135+
token, err := packages_service.CreateAuthorizationToken(ctx.Doer, packageScope)
121136
if err != nil {
122137
apiError(ctx, http.StatusInternalServerError, err)
123138
return
@@ -130,9 +145,23 @@ func Authenticate(ctx *context.Context) {
130145
func CheckCredentials(ctx *context.Context) {
131146
if ctx.Doer == nil {
132147
ctx.Status(http.StatusUnauthorized)
133-
} else {
134-
ctx.Status(http.StatusOK)
148+
return
135149
}
150+
151+
packageScope := auth_service.GetAccessScope(ctx.Data)
152+
if has, err := packageScope.HasAnyScope(
153+
auth_model.AccessTokenScopeReadPackage,
154+
auth_model.AccessTokenScopeWritePackage,
155+
auth_model.AccessTokenScopeAll,
156+
); !has {
157+
if err != nil {
158+
log.Error("Error checking access scope: %v", err)
159+
}
160+
ctx.Status(http.StatusForbidden)
161+
return
162+
}
163+
164+
ctx.Status(http.StatusOK)
136165
}
137166

138167
// RecipeSnapshot displays the recipe files with their md5 hash

routers/api/packages/container/auth.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,21 +23,26 @@ func (a *Auth) Name() string {
2323
// Verify extracts the user from the Bearer token
2424
// If it's an anonymous session a ghost user is returned
2525
func (a *Auth) Verify(req *http.Request, w http.ResponseWriter, store auth.DataStore, sess auth.SessionStore) (*user_model.User, error) {
26-
uid, err := packages.ParseAuthorizationToken(req)
26+
packageMeta, err := packages.ParseAuthorizationRequest(req)
2727
if err != nil {
2828
log.Trace("ParseAuthorizationToken: %v", err)
2929
return nil, err
3030
}
3131

32-
if uid == 0 {
32+
if packageMeta == nil || packageMeta.UserID == 0 {
3333
return nil, nil
3434
}
3535

36-
u, err := user_model.GetPossibleUserByID(req.Context(), uid)
36+
u, err := user_model.GetPossibleUserByID(req.Context(), packageMeta.UserID)
3737
if err != nil {
3838
log.Error("GetPossibleUserByID: %v", err)
3939
return nil, err
4040
}
4141

42+
if packageMeta.Scope != "" {
43+
store.GetData()["IsApiToken"] = true
44+
store.GetData()["ApiTokenScope"] = packageMeta.Scope
45+
}
46+
4247
return u, nil
4348
}

routers/api/packages/container/container.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"strconv"
1515
"strings"
1616

17+
auth_model "code.gitea.io/gitea/models/auth"
1718
packages_model "code.gitea.io/gitea/models/packages"
1819
container_model "code.gitea.io/gitea/models/packages/container"
1920
user_model "code.gitea.io/gitea/models/user"
@@ -25,6 +26,7 @@ import (
2526
"code.gitea.io/gitea/modules/setting"
2627
"code.gitea.io/gitea/modules/util"
2728
"code.gitea.io/gitea/routers/api/packages/helper"
29+
auth_service "code.gitea.io/gitea/services/auth"
2830
"code.gitea.io/gitea/services/context"
2931
packages_service "code.gitea.io/gitea/services/packages"
3032
container_service "code.gitea.io/gitea/services/packages/container"
@@ -148,16 +150,29 @@ func DetermineSupport(ctx *context.Context) {
148150
// If the current user is anonymous, the ghost user is used unless RequireSignInView is enabled.
149151
func Authenticate(ctx *context.Context) {
150152
u := ctx.Doer
153+
packageScope := auth_service.GetAccessScope(ctx.Data)
151154
if u == nil {
152155
if setting.Service.RequireSignInView {
153156
apiUnauthorizedError(ctx)
154157
return
155158
}
156159

157160
u = user_model.NewGhostUser()
161+
} else {
162+
if has, err := packageScope.HasAnyScope(
163+
auth_model.AccessTokenScopeReadPackage,
164+
auth_model.AccessTokenScopeWritePackage,
165+
auth_model.AccessTokenScopeAll,
166+
); !has {
167+
if err != nil {
168+
log.Error("Error checking access scope: %v", err)
169+
}
170+
apiUnauthorizedError(ctx)
171+
return
172+
}
158173
}
159174

160-
token, err := packages_service.CreateAuthorizationToken(u)
175+
token, err := packages_service.CreateAuthorizationToken(u, packageScope)
161176
if err != nil {
162177
apiError(ctx, http.StatusInternalServerError, err)
163178
return

routers/api/packages/nuget/auth.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,5 +43,8 @@ func (a *Auth) Verify(req *http.Request, w http.ResponseWriter, store auth.DataS
4343
log.Error("UpdateAccessToken: %v", err)
4444
}
4545

46+
store.GetData()["IsApiToken"] = true
47+
store.GetData()["ApiToken"] = token
48+
4649
return u, nil
4750
}

services/auth/basic.go

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,12 @@ var (
2525
)
2626

2727
// BasicMethodName is the constant name of the basic authentication method
28-
const BasicMethodName = "basic"
28+
const (
29+
BasicMethodName = "basic"
30+
AccessTokenMethodName = "access_token"
31+
OAuth2TokenMethodName = "oauth2_token"
32+
ActionTokenMethodName = "action_token"
33+
)
2934

3035
// Basic implements the Auth interface and authenticates requests (API requests
3136
// only) by looking for Basic authentication data or "x-oauth-basic" token in the "Authorization"
@@ -82,6 +87,7 @@ func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore
8287
return nil, err
8388
}
8489

90+
store.GetData()["LoginMethod"] = OAuth2TokenMethodName
8591
store.GetData()["IsApiToken"] = true
8692
return u, nil
8793
}
@@ -101,6 +107,7 @@ func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore
101107
log.Error("UpdateAccessToken: %v", err)
102108
}
103109

110+
store.GetData()["LoginMethod"] = AccessTokenMethodName
104111
store.GetData()["IsApiToken"] = true
105112
store.GetData()["ApiTokenScope"] = token.Scope
106113
return u, nil
@@ -113,6 +120,7 @@ func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore
113120
if err == nil && task != nil {
114121
log.Trace("Basic Authorization: Valid AccessToken for task[%d]", task.ID)
115122

123+
store.GetData()["LoginMethod"] = ActionTokenMethodName
116124
store.GetData()["IsActionsToken"] = true
117125
store.GetData()["ActionsTaskID"] = task.ID
118126

@@ -138,6 +146,7 @@ func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore
138146
}
139147
}
140148

149+
store.GetData()["LoginMethod"] = BasicMethodName
141150
log.Trace("Basic Authorization: Logged in user %-v", u)
142151

143152
return u, nil
@@ -159,3 +168,19 @@ func validateTOTP(req *http.Request, u *user_model.User) error {
159168
}
160169
return nil
161170
}
171+
172+
func GetAccessScope(store DataStore) auth_model.AccessTokenScope {
173+
if v, ok := store.GetData()["ApiTokenScope"]; ok {
174+
return v.(auth_model.AccessTokenScope)
175+
}
176+
switch store.GetData()["LoginMethod"] {
177+
case OAuth2TokenMethodName:
178+
fallthrough
179+
case BasicMethodName, AccessTokenMethodName:
180+
return auth_model.AccessTokenScopeAll
181+
case ActionTokenMethodName:
182+
fallthrough
183+
default:
184+
return ""
185+
}
186+
}

services/packages/auth.go

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"strings"
1010
"time"
1111

12+
auth_model "code.gitea.io/gitea/models/auth"
1213
user_model "code.gitea.io/gitea/models/user"
1314
"code.gitea.io/gitea/modules/log"
1415
"code.gitea.io/gitea/modules/setting"
@@ -18,18 +19,25 @@ import (
1819

1920
type packageClaims struct {
2021
jwt.RegisteredClaims
22+
PackageMeta
23+
}
24+
type PackageMeta struct {
2125
UserID int64
26+
Scope auth_model.AccessTokenScope
2227
}
2328

24-
func CreateAuthorizationToken(u *user_model.User) (string, error) {
29+
func CreateAuthorizationToken(u *user_model.User, packageScope auth_model.AccessTokenScope) (string, error) {
2530
now := time.Now()
2631

2732
claims := packageClaims{
2833
RegisteredClaims: jwt.RegisteredClaims{
2934
ExpiresAt: jwt.NewNumericDate(now.Add(24 * time.Hour)),
3035
NotBefore: jwt.NewNumericDate(now),
3136
},
32-
UserID: u.ID,
37+
PackageMeta: PackageMeta{
38+
UserID: u.ID,
39+
Scope: packageScope,
40+
},
3341
}
3442
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
3543

@@ -41,32 +49,36 @@ func CreateAuthorizationToken(u *user_model.User) (string, error) {
4149
return tokenString, nil
4250
}
4351

44-
func ParseAuthorizationToken(req *http.Request) (int64, error) {
52+
func ParseAuthorizationRequest(req *http.Request) (*PackageMeta, error) {
4553
h := req.Header.Get("Authorization")
4654
if h == "" {
47-
return 0, nil
55+
return nil, nil
4856
}
4957

5058
parts := strings.SplitN(h, " ", 2)
5159
if len(parts) != 2 {
5260
log.Error("split token failed: %s", h)
53-
return 0, fmt.Errorf("split token failed")
61+
return nil, fmt.Errorf("split token failed")
5462
}
5563

56-
token, err := jwt.ParseWithClaims(parts[1], &packageClaims{}, func(t *jwt.Token) (any, error) {
64+
return ParseAuthorizationToken(parts[1])
65+
}
66+
67+
func ParseAuthorizationToken(tokenStr string) (*PackageMeta, error) {
68+
token, err := jwt.ParseWithClaims(tokenStr, &packageClaims{}, func(t *jwt.Token) (any, error) {
5769
if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
5870
return nil, fmt.Errorf("unexpected signing method: %v", t.Header["alg"])
5971
}
6072
return setting.GetGeneralTokenSigningSecret(), nil
6173
})
6274
if err != nil {
63-
return 0, err
75+
return nil, err
6476
}
6577

6678
c, ok := token.Claims.(*packageClaims)
6779
if !token.Valid || !ok {
68-
return 0, fmt.Errorf("invalid token claim")
80+
return nil, fmt.Errorf("invalid token claim")
6981
}
7082

71-
return c.UserID, nil
83+
return &c.PackageMeta, nil
7284
}

0 commit comments

Comments
 (0)