0
0
mirror of https://github.com/go-gitea/gitea.git synced 2025-07-24 10:44:32 -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" "code.gitea.io/gitea/modules/util"
) )
func ParseAuthorizationHeaderBasic(header string) (string, string, bool) { type BasicAuth struct {
parts := strings.Fields(header) Username, Password string
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
} }
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) parts := strings.Fields(header)
if len(parts) != 2 { if len(parts) != 2 {
return "", false return ret, false
} }
if util.AsciiEqualFold(parts[0], "token") || util.AsciiEqualFold(parts[0], "bearer") { if util.AsciiEqualFold(parts[0], "basic") {
return parts[1], true 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) { func TestParseAuthorizationHeader(t *testing.T) {
t.Run("Basic", func(t *testing.T) { type parsed = ParsedAuthorizationHeader
cases := []struct { type basic = BasicAuth
headerValue string type bearer = BearerToken
user, pass string cases := []struct {
ok bool headerValue string
}{ expected parsed
{"", "", "", false}, ok bool
{"?", "", "", false}, }{
{"foo", "", "", false}, {"", parsed{}, false},
{"Basic ?", "", "", false}, {"?", parsed{}, false},
{"Basic " + base64.StdEncoding.EncodeToString([]byte("foo")), "", "", false}, {"foo", parsed{}, false},
{"Basic " + base64.StdEncoding.EncodeToString([]byte("foo:bar")), "foo", "bar", true}, {"any value", parsed{}, false},
{"basic " + base64.StdEncoding.EncodeToString([]byte("foo:bar")), "foo", "bar", true},
} {"Basic ?", parsed{}, false},
for _, c := range cases { {"Basic " + base64.StdEncoding.EncodeToString([]byte("foo")), parsed{}, false},
user, pass, ok := ParseAuthorizationHeaderBasic(c.headerValue) {"Basic " + base64.StdEncoding.EncodeToString([]byte("foo:bar")), parsed{BasicAuth: &basic{"foo", "bar"}}, true},
assert.Equal(t, c.ok, ok, "header %q", c.headerValue) {"basic " + base64.StdEncoding.EncodeToString([]byte("foo:bar")), parsed{BasicAuth: &basic{"foo", "bar"}}, true},
assert.Equal(t, c.user, user, "header %q", c.headerValue)
assert.Equal(t, c.pass, pass, "header %q", c.headerValue) {"token value", parsed{BearerToken: &bearer{"value"}}, true},
} {"Token value", parsed{BearerToken: &bearer{"value"}}, true},
}) {"bearer value", parsed{BearerToken: &bearer{"value"}}, true},
t.Run("BearerToken", func(t *testing.T) { {"Bearer value", parsed{BearerToken: &bearer{"value"}}, true},
cases := []struct { {"Bearer wrong value", parsed{}, false},
headerValue string }
expected string for _, c := range cases {
ok bool ret, ok := ParseAuthorizationHeader(c.headerValue)
}{ assert.Equal(t, c.ok, ok, "header %q", c.headerValue)
{"", "", false}, assert.Equal(t, c.expected, ret, "header %q", c.headerValue)
{"?", "", 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)
}
})
} }

View File

@ -106,8 +106,8 @@ func InfoOAuth(ctx *context.Context) {
var accessTokenScope auth.AccessTokenScope var accessTokenScope auth.AccessTokenScope
if auHead := ctx.Req.Header.Get("Authorization"); auHead != "" { if auHead := ctx.Req.Header.Get("Authorization"); auHead != "" {
if headerAuthToken, ok := httpauth.ParseAuthorizationHeaderBearerToken(auHead); ok { if parsed, ok := httpauth.ParseAuthorizationHeader(auHead); ok && parsed.BearerToken != nil {
accessTokenScope, _ = auth_service.GetOAuthAccessTokenScopeAndUserID(ctx, headerAuthToken) accessTokenScope, _ = auth_service.GetOAuthAccessTokenScopeAndUserID(ctx, parsed.BearerToken.Token)
} }
} }
@ -128,7 +128,8 @@ func InfoOAuth(ctx *context.Context) {
func IntrospectOAuth(ctx *context.Context) { func IntrospectOAuth(ctx *context.Context) {
clientIDValid := false clientIDValid := false
authHeader := ctx.Req.Header.Get("Authorization") 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) app, err := auth.GetOAuth2ApplicationByClientID(ctx, clientID)
if err != nil && !auth.IsErrOauthClientIDInvalid(err) { if err != nil && !auth.IsErrOauthClientIDInvalid(err) {
// this is likely a database error; log it and respond without details // 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 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 form.ClientID == "" || form.ClientSecret == "" {
if authHeader := ctx.Req.Header.Get("Authorization"); authHeader != "" { if authHeader := ctx.Req.Header.Get("Authorization"); authHeader != "" {
clientID, clientSecret, ok := httpauth.ParseAuthorizationHeaderBasic(authHeader) parsed, ok := httpauth.ParseAuthorizationHeader(authHeader)
if !ok { if !ok || parsed.BasicAuth == nil {
handleAccessTokenError(ctx, oauth2_provider.AccessTokenError{ handleAccessTokenError(ctx, oauth2_provider.AccessTokenError{
ErrorCode: oauth2_provider.AccessTokenErrorCodeInvalidRequest, ErrorCode: oauth2_provider.AccessTokenErrorCodeInvalidRequest,
ErrorDescription: "cannot parse basic auth header", ErrorDescription: "cannot parse basic auth header",
}) })
return return
} }
clientID, clientSecret := parsed.BasicAuth.Username, parsed.BasicAuth.Password
// validate that any fields present in the form match the Basic auth header // validate that any fields present in the form match the Basic auth header
if form.ClientID != "" && form.ClientID != clientID { if form.ClientID != "" && form.ClientID != clientID {
handleAccessTokenError(ctx, oauth2_provider.AccessTokenError{ 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 == "" { if authHeader == "" {
return nil, nil 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 // Check if username or password is a token
isUsernameToken := len(passwd) == 0 || passwd == "x-oauth-basic" isUsernameToken := len(passwd) == 0 || passwd == "x-oauth-basic"

View File

@ -98,7 +98,10 @@ func parseToken(req *http.Request) (string, bool) {
// check header token // check header token
if auHead := req.Header.Get("Authorization"); auHead != "" { 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 return "", false
} }

View File

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