mirror of
https://codeberg.org/forgejo/forgejo
synced 2024-11-22 09:54:24 +01:00
5a871f6095
- The Conan and Container packages use a different type of authentication. It first authenticates via the regular way (api tokens or user:password, handled via `auth.Basic`) and then generates a JWT token that is used by the package software (such as Docker) to do the action they wanted to do. This JWT token didn't properly propagate the API scopes that the token was generated for, and thus could lead to a 'scope escalation' within the Conan and Container packages, read access to write access. - Store the API scope in the JWT token, so it can be propagated on subsequent calls that uses that JWT token. - Integration test added. - Resolves #5128
76 lines
1.8 KiB
Go
76 lines
1.8 KiB
Go
// Copyright 2022 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package packages
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
auth_model "code.gitea.io/gitea/models/auth"
|
|
user_model "code.gitea.io/gitea/models/user"
|
|
"code.gitea.io/gitea/modules/log"
|
|
"code.gitea.io/gitea/modules/setting"
|
|
|
|
"github.com/golang-jwt/jwt/v5"
|
|
)
|
|
|
|
type packageClaims struct {
|
|
jwt.RegisteredClaims
|
|
UserID int64
|
|
Scope auth_model.AccessTokenScope
|
|
}
|
|
|
|
func CreateAuthorizationToken(u *user_model.User, scope auth_model.AccessTokenScope) (string, error) {
|
|
now := time.Now()
|
|
|
|
claims := packageClaims{
|
|
RegisteredClaims: jwt.RegisteredClaims{
|
|
ExpiresAt: jwt.NewNumericDate(now.Add(24 * time.Hour)),
|
|
NotBefore: jwt.NewNumericDate(now),
|
|
},
|
|
UserID: u.ID,
|
|
Scope: scope,
|
|
}
|
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
|
|
|
tokenString, err := token.SignedString(setting.GetGeneralTokenSigningSecret())
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return tokenString, nil
|
|
}
|
|
|
|
func ParseAuthorizationToken(req *http.Request) (int64, auth_model.AccessTokenScope, error) {
|
|
h := req.Header.Get("Authorization")
|
|
if h == "" {
|
|
return 0, "", nil
|
|
}
|
|
|
|
parts := strings.SplitN(h, " ", 2)
|
|
if len(parts) != 2 {
|
|
log.Error("split token failed: %s", h)
|
|
return 0, "", fmt.Errorf("split token failed")
|
|
}
|
|
|
|
token, err := jwt.ParseWithClaims(parts[1], &packageClaims{}, func(t *jwt.Token) (any, error) {
|
|
if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
|
|
return nil, fmt.Errorf("unexpected signing method: %v", t.Header["alg"])
|
|
}
|
|
return setting.GetGeneralTokenSigningSecret(), nil
|
|
})
|
|
if err != nil {
|
|
return 0, "", err
|
|
}
|
|
|
|
c, ok := token.Claims.(*packageClaims)
|
|
if !token.Valid || !ok {
|
|
return 0, "", fmt.Errorf("invalid token claim")
|
|
}
|
|
|
|
return c.UserID, c.Scope, nil
|
|
}
|