0
0
mirror of https://github.com/go-gitea/gitea.git synced 2025-07-04 22:57:34 -04:00

fix parser

This commit is contained in:
wxiaoguang 2025-07-03 09:39:43 +08:00
parent b38e25b96b
commit aefac78df7
6 changed files with 76 additions and 73 deletions

View File

@ -10,31 +10,38 @@ import (
"code.gitea.io/gitea/modules/util"
)
func ParseAuthorizationHeaderBasic(header string) (string, string, bool) {
parts := strings.Fields(header)
if len(parts) != 2 {
return "", "", false
}
if !util.AsciiEqualFold(parts[0], "basic") {
return "", "", false
}
s, err := base64.StdEncoding.DecodeString(parts[1])
if err != nil {
return "", "", false
}
if u, p, ok := strings.Cut(string(s), ":"); ok {
return u, p, true
}
return "", "", false
type BasicAuth struct {
Username, Password string
}
func ParseAuthorizationHeaderBearerToken(header string) (string, bool) {
type BearerToken struct {
Token string
}
type ParsedAuthorizationHeader struct {
BasicAuth *BasicAuth
BearerToken *BearerToken
}
func ParseAuthorizationHeader(header string) (ret ParsedAuthorizationHeader, _ bool) {
parts := strings.Fields(header)
if len(parts) != 2 {
return "", false
return ret, false
}
if util.AsciiEqualFold(parts[0], "token") || util.AsciiEqualFold(parts[0], "bearer") {
return parts[1], true
if util.AsciiEqualFold(parts[0], "basic") {
s, err := base64.StdEncoding.DecodeString(parts[1])
if err != nil {
return ret, false
}
u, p, ok := strings.Cut(string(s), ":")
if !ok {
return ret, false
}
ret.BasicAuth = &BasicAuth{Username: u, Password: p}
return ret, true
} else if util.AsciiEqualFold(parts[0], "token") || util.AsciiEqualFold(parts[0], "bearer") {
ret.BearerToken = &BearerToken{Token: parts[1]}
return ret, true
}
return "", false
return ret, false
}

View File

@ -11,46 +11,33 @@ import (
)
func TestParseAuthorizationHeader(t *testing.T) {
t.Run("Basic", func(t *testing.T) {
cases := []struct {
headerValue string
user, pass string
ok bool
}{
{"", "", "", false},
{"?", "", "", false},
{"foo", "", "", false},
{"Basic ?", "", "", false},
{"Basic " + base64.StdEncoding.EncodeToString([]byte("foo")), "", "", false},
{"Basic " + base64.StdEncoding.EncodeToString([]byte("foo:bar")), "foo", "bar", true},
{"basic " + base64.StdEncoding.EncodeToString([]byte("foo:bar")), "foo", "bar", true},
}
for _, c := range cases {
user, pass, ok := ParseAuthorizationHeaderBasic(c.headerValue)
assert.Equal(t, c.ok, ok, "header %q", c.headerValue)
assert.Equal(t, c.user, user, "header %q", c.headerValue)
assert.Equal(t, c.pass, pass, "header %q", c.headerValue)
}
})
t.Run("BearerToken", func(t *testing.T) {
cases := []struct {
headerValue string
expected string
ok bool
}{
{"", "", false},
{"?", "", false},
{"any value", "", false},
{"token value", "value", true},
{"Token value", "value", true},
{"bearer value", "value", true},
{"Bearer value", "value", true},
{"Bearer wrong value", "", false},
}
for _, c := range cases {
token, ok := ParseAuthorizationHeaderBearerToken(c.headerValue)
assert.Equal(t, c.ok, ok, "header %q", c.headerValue)
assert.Equal(t, c.expected, token, "header %q", c.headerValue)
}
})
type parsed = ParsedAuthorizationHeader
type basic = BasicAuth
type bearer = BearerToken
cases := []struct {
headerValue string
expected parsed
ok bool
}{
{"", parsed{}, false},
{"?", parsed{}, false},
{"foo", parsed{}, false},
{"any value", parsed{}, false},
{"Basic ?", parsed{}, false},
{"Basic " + base64.StdEncoding.EncodeToString([]byte("foo")), parsed{}, false},
{"Basic " + base64.StdEncoding.EncodeToString([]byte("foo:bar")), parsed{BasicAuth: &basic{"foo", "bar"}}, true},
{"basic " + base64.StdEncoding.EncodeToString([]byte("foo:bar")), parsed{BasicAuth: &basic{"foo", "bar"}}, true},
{"token value", parsed{BearerToken: &bearer{"value"}}, true},
{"Token value", parsed{BearerToken: &bearer{"value"}}, true},
{"bearer value", parsed{BearerToken: &bearer{"value"}}, true},
{"Bearer value", parsed{BearerToken: &bearer{"value"}}, true},
{"Bearer wrong value", parsed{}, false},
}
for _, c := range cases {
ret, ok := ParseAuthorizationHeader(c.headerValue)
assert.Equal(t, c.ok, ok, "header %q", c.headerValue)
assert.Equal(t, c.expected, ret, "header %q", c.headerValue)
}
}

View File

@ -106,8 +106,8 @@ func InfoOAuth(ctx *context.Context) {
var accessTokenScope auth.AccessTokenScope
if auHead := ctx.Req.Header.Get("Authorization"); auHead != "" {
if headerAuthToken, ok := httpauth.ParseAuthorizationHeaderBearerToken(auHead); ok {
accessTokenScope, _ = auth_service.GetOAuthAccessTokenScopeAndUserID(ctx, headerAuthToken)
if parsed, ok := httpauth.ParseAuthorizationHeader(auHead); ok && parsed.BearerToken != nil {
accessTokenScope, _ = auth_service.GetOAuthAccessTokenScopeAndUserID(ctx, parsed.BearerToken.Token)
}
}
@ -128,7 +128,8 @@ func InfoOAuth(ctx *context.Context) {
func IntrospectOAuth(ctx *context.Context) {
clientIDValid := false
authHeader := ctx.Req.Header.Get("Authorization")
if clientID, clientSecret, ok := httpauth.ParseAuthorizationHeaderBasic(authHeader); ok {
if parsed, ok := httpauth.ParseAuthorizationHeader(authHeader); ok && parsed.BasicAuth != nil {
clientID, clientSecret := parsed.BasicAuth.Username, parsed.BasicAuth.Password
app, err := auth.GetOAuth2ApplicationByClientID(ctx, clientID)
if err != nil && !auth.IsErrOauthClientIDInvalid(err) {
// this is likely a database error; log it and respond without details
@ -456,14 +457,15 @@ func AccessTokenOAuth(ctx *context.Context) {
// if there is no ClientID or ClientSecret in the request body, fill these fields by the Authorization header and ensure the provided field matches the Authorization header
if form.ClientID == "" || form.ClientSecret == "" {
if authHeader := ctx.Req.Header.Get("Authorization"); authHeader != "" {
clientID, clientSecret, ok := httpauth.ParseAuthorizationHeaderBasic(authHeader)
if !ok {
parsed, ok := httpauth.ParseAuthorizationHeader(authHeader)
if !ok || parsed.BasicAuth == nil {
handleAccessTokenError(ctx, oauth2_provider.AccessTokenError{
ErrorCode: oauth2_provider.AccessTokenErrorCodeInvalidRequest,
ErrorDescription: "cannot parse basic auth header",
})
return
}
clientID, clientSecret := parsed.BasicAuth.Username, parsed.BasicAuth.Password
// validate that any fields present in the form match the Basic auth header
if form.ClientID != "" && form.ClientID != clientID {
handleAccessTokenError(ctx, oauth2_provider.AccessTokenError{

View File

@ -57,7 +57,11 @@ func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore
if authHeader == "" {
return nil, nil
}
uname, passwd, _ := httpauth.ParseAuthorizationHeaderBasic(authHeader)
parsed, ok := httpauth.ParseAuthorizationHeader(authHeader)
if !ok || parsed.BasicAuth == nil {
return nil, nil
}
uname, passwd := parsed.BasicAuth.Username, parsed.BasicAuth.Password
// Check if username or password is a token
isUsernameToken := len(passwd) == 0 || passwd == "x-oauth-basic"

View File

@ -98,7 +98,10 @@ func parseToken(req *http.Request) (string, bool) {
// check header token
if auHead := req.Header.Get("Authorization"); auHead != "" {
return httpauth.ParseAuthorizationHeaderBearerToken(auHead)
parsed, ok := httpauth.ParseAuthorizationHeader(auHead)
if ok && parsed.BearerToken != nil {
return parsed.BearerToken.Token, true
}
}
return "", false
}

View File

@ -595,11 +595,11 @@ func parseToken(ctx stdCtx.Context, authorization string, target *repo_model.Rep
if authorization == "" {
return nil, errors.New("no token")
}
token, ok := httpauth.ParseAuthorizationHeaderBearerToken(authorization)
if !ok {
parsed, ok := httpauth.ParseAuthorizationHeader(authorization)
if !ok || parsed.BearerToken == nil {
return nil, errors.New("token not found")
}
return handleLFSToken(ctx, token, target, mode)
return handleLFSToken(ctx, parsed.BearerToken.Token, target, mode)
}
func requireAuth(ctx *context.Context) {