mirror of
https://github.com/go-gitea/gitea.git
synced 2026-05-21 08:50:51 +02:00
Fix basic auth bug (#37486)
This commit is contained in:
parent
1721c235a7
commit
48cea1fb79
@ -69,7 +69,7 @@ func (b *Basic) parseAuthBasic(req *http.Request) (ret struct{ authToken, uname,
|
|||||||
// VerifyAuthToken only the access token provided as parameter, used by other auth methods that want to reuse access token verification logic
|
// VerifyAuthToken only the access token provided as parameter, used by other auth methods that want to reuse access token verification logic
|
||||||
func (b *Basic) VerifyAuthToken(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore, authToken string) (*user_model.User, error) {
|
func (b *Basic) VerifyAuthToken(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore, authToken string) (*user_model.User, error) {
|
||||||
// get oauth2 token's user's ID
|
// get oauth2 token's user's ID
|
||||||
_, uid := GetOAuthAccessTokenScopeAndUserID(req.Context(), authToken)
|
accessTokenScope, uid := GetOAuthAccessTokenScopeAndUserID(req.Context(), authToken)
|
||||||
if uid != 0 {
|
if uid != 0 {
|
||||||
log.Trace("Basic Authorization: Valid OAuthAccessToken for user[%d]", uid)
|
log.Trace("Basic Authorization: Valid OAuthAccessToken for user[%d]", uid)
|
||||||
|
|
||||||
@ -81,6 +81,7 @@ func (b *Basic) VerifyAuthToken(req *http.Request, w http.ResponseWriter, store
|
|||||||
|
|
||||||
store.GetData()["LoginMethod"] = OAuth2TokenMethodName
|
store.GetData()["LoginMethod"] = OAuth2TokenMethodName
|
||||||
store.GetData()["IsApiToken"] = true
|
store.GetData()["IsApiToken"] = true
|
||||||
|
store.GetData()["ApiTokenScope"] = accessTokenScope
|
||||||
return u, nil
|
return u, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -78,6 +78,7 @@ func TestOAuth2(t *testing.T) {
|
|||||||
t.Run("RefreshTokenInvalidation", testRefreshTokenInvalidation)
|
t.Run("RefreshTokenInvalidation", testRefreshTokenInvalidation)
|
||||||
t.Run("OAuthIntrospection", testOAuthIntrospection)
|
t.Run("OAuthIntrospection", testOAuthIntrospection)
|
||||||
t.Run("OAuthGrantScopesReadUserFailRepos", testOAuthGrantScopesReadUserFailRepos)
|
t.Run("OAuthGrantScopesReadUserFailRepos", testOAuthGrantScopesReadUserFailRepos)
|
||||||
|
t.Run("OAuthGrantScopesBasicRespectsWriteUser", testOAuthGrantScopesBasicRespectsWriteUser)
|
||||||
t.Run("OAuthGrantScopesReadRepositoryFailOrganization", testOAuthGrantScopesReadRepositoryFailOrganization)
|
t.Run("OAuthGrantScopesReadRepositoryFailOrganization", testOAuthGrantScopesReadRepositoryFailOrganization)
|
||||||
t.Run("OAuthGrantScopesClaimPublicOnlyGroups", testOAuthGrantScopesClaimPublicOnlyGroups)
|
t.Run("OAuthGrantScopesClaimPublicOnlyGroups", testOAuthGrantScopesClaimPublicOnlyGroups)
|
||||||
t.Run("OAuthGrantScopesClaimAllGroups", testOAuthGrantScopesClaimAllGroups)
|
t.Run("OAuthGrantScopesClaimAllGroups", testOAuthGrantScopesClaimAllGroups)
|
||||||
@ -582,6 +583,67 @@ func testOAuthIntrospection(t *testing.T) {
|
|||||||
|
|
||||||
func testOAuthGrantScopesReadUserFailRepos(t *testing.T) {
|
func testOAuthGrantScopesReadUserFailRepos(t *testing.T) {
|
||||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||||
|
accessToken := issueOAuthAccessTokenForScope(t, user, "openid read:user")
|
||||||
|
userReq := NewRequest(t, "GET", "/api/v1/user")
|
||||||
|
userReq.SetHeader("Authorization", "Bearer "+accessToken)
|
||||||
|
userResp := MakeRequest(t, userReq, http.StatusOK)
|
||||||
|
|
||||||
|
type userResponse struct {
|
||||||
|
Login string `json:"login"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
}
|
||||||
|
|
||||||
|
userParsed := new(userResponse)
|
||||||
|
require.NoError(t, json.Unmarshal(userResp.Body.Bytes(), userParsed))
|
||||||
|
assert.Contains(t, userParsed.Email, "user2@example.com")
|
||||||
|
|
||||||
|
errorReq := NewRequest(t, "GET", "/api/v1/users/user2/repos")
|
||||||
|
errorReq.SetHeader("Authorization", "Bearer "+accessToken)
|
||||||
|
errorResp := MakeRequest(t, errorReq, http.StatusForbidden)
|
||||||
|
|
||||||
|
type errorResponse struct {
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
errorParsed := new(errorResponse)
|
||||||
|
require.NoError(t, json.Unmarshal(errorResp.Body.Bytes(), errorParsed))
|
||||||
|
assert.Contains(t, errorParsed.Message, "token does not have at least one of required scope(s), required=[read:repository]")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testOAuthGrantScopesBasicRespectsWriteUser(t *testing.T) {
|
||||||
|
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||||
|
accessToken := issueOAuthAccessTokenForScope(t, user, "openid read:user")
|
||||||
|
fullName := "oauth2-basic-scope-test"
|
||||||
|
|
||||||
|
updateBody := &api.UserSettingsOptions{
|
||||||
|
FullName: &fullName,
|
||||||
|
}
|
||||||
|
|
||||||
|
bearerReq := NewRequestWithJSON(t, "PATCH", "/api/v1/user/settings", updateBody)
|
||||||
|
bearerReq.SetHeader("Authorization", "Bearer "+accessToken)
|
||||||
|
bearerResp := MakeRequest(t, bearerReq, http.StatusForbidden)
|
||||||
|
|
||||||
|
type errorResponse struct {
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
bearerError := new(errorResponse)
|
||||||
|
require.NoError(t, json.Unmarshal(bearerResp.Body.Bytes(), bearerError))
|
||||||
|
assert.Contains(t, bearerError.Message, "required=[write:user]")
|
||||||
|
|
||||||
|
basicReq := NewRequestWithJSON(t, "PATCH", "/api/v1/user/settings", updateBody)
|
||||||
|
basicAuth := base64.StdEncoding.EncodeToString([]byte(accessToken + ":x-oauth-basic"))
|
||||||
|
basicReq.SetHeader("Authorization", "Basic "+basicAuth)
|
||||||
|
basicResp := MakeRequest(t, basicReq, http.StatusForbidden)
|
||||||
|
|
||||||
|
basicError := new(errorResponse)
|
||||||
|
require.NoError(t, json.Unmarshal(basicResp.Body.Bytes(), basicError))
|
||||||
|
assert.Contains(t, basicError.Message, "required=[write:user]")
|
||||||
|
}
|
||||||
|
|
||||||
|
func issueOAuthAccessTokenForScope(t *testing.T, user *user_model.User, scope string) string {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
appBody := api.CreateOAuth2ApplicationOptions{
|
appBody := api.CreateOAuth2ApplicationOptions{
|
||||||
Name: "oauth-provider-scopes-test",
|
Name: "oauth-provider-scopes-test",
|
||||||
RedirectURIs: []string{
|
RedirectURIs: []string{
|
||||||
@ -599,20 +661,14 @@ func testOAuthGrantScopesReadUserFailRepos(t *testing.T) {
|
|||||||
grant := &auth_model.OAuth2Grant{
|
grant := &auth_model.OAuth2Grant{
|
||||||
ApplicationID: app.ID,
|
ApplicationID: app.ID,
|
||||||
UserID: user.ID,
|
UserID: user.ID,
|
||||||
Scope: "openid read:user",
|
Scope: scope,
|
||||||
}
|
}
|
||||||
|
require.NoError(t, db.Insert(t.Context(), grant))
|
||||||
err := db.Insert(t.Context(), grant)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assert.Contains(t, grant.Scope, "openid read:user")
|
|
||||||
|
|
||||||
ctx := loginUser(t, user.Name)
|
ctx := loginUser(t, user.Name)
|
||||||
|
|
||||||
authorizeURL := fmt.Sprintf("/login/oauth/authorize?client_id=%s&redirect_uri=https://example.com&response_type=code&state=thestate", app.ClientID)
|
authorizeURL := fmt.Sprintf("/login/oauth/authorize?client_id=%s&redirect_uri=https://example.com&response_type=code&state=thestate", app.ClientID)
|
||||||
authorizeReq := NewRequest(t, "GET", authorizeURL)
|
authorizeReq := NewRequest(t, "GET", authorizeURL)
|
||||||
authorizeResp := ctx.MakeRequest(t, authorizeReq, http.StatusSeeOther)
|
authorizeResp := ctx.MakeRequest(t, authorizeReq, http.StatusSeeOther)
|
||||||
|
|
||||||
authcode := strings.Split(strings.Split(authorizeResp.Body.String(), "?code=")[1], "&")[0]
|
authcode := strings.Split(strings.Split(authorizeResp.Body.String(), "?code=")[1], "&")[0]
|
||||||
|
|
||||||
accessTokenReq := NewRequestWithValues(t, "POST", "/login/oauth/access_token", map[string]string{
|
accessTokenReq := NewRequestWithValues(t, "POST", "/login/oauth/access_token", map[string]string{
|
||||||
@ -622,7 +678,7 @@ func testOAuthGrantScopesReadUserFailRepos(t *testing.T) {
|
|||||||
"redirect_uri": "https://example.com",
|
"redirect_uri": "https://example.com",
|
||||||
"code": authcode,
|
"code": authcode,
|
||||||
})
|
})
|
||||||
accessTokenResp := ctx.MakeRequest(t, accessTokenReq, 200)
|
accessTokenResp := ctx.MakeRequest(t, accessTokenReq, http.StatusOK)
|
||||||
type response struct {
|
type response struct {
|
||||||
AccessToken string `json:"access_token"`
|
AccessToken string `json:"access_token"`
|
||||||
TokenType string `json:"token_type"`
|
TokenType string `json:"token_type"`
|
||||||
@ -630,32 +686,8 @@ func testOAuthGrantScopesReadUserFailRepos(t *testing.T) {
|
|||||||
RefreshToken string `json:"refresh_token"`
|
RefreshToken string `json:"refresh_token"`
|
||||||
}
|
}
|
||||||
parsed := new(response)
|
parsed := new(response)
|
||||||
|
|
||||||
require.NoError(t, json.Unmarshal(accessTokenResp.Body.Bytes(), parsed))
|
require.NoError(t, json.Unmarshal(accessTokenResp.Body.Bytes(), parsed))
|
||||||
userReq := NewRequest(t, "GET", "/api/v1/user")
|
return parsed.AccessToken
|
||||||
userReq.SetHeader("Authorization", "Bearer "+parsed.AccessToken)
|
|
||||||
userResp := MakeRequest(t, userReq, http.StatusOK)
|
|
||||||
|
|
||||||
type userResponse struct {
|
|
||||||
Login string `json:"login"`
|
|
||||||
Email string `json:"email"`
|
|
||||||
}
|
|
||||||
|
|
||||||
userParsed := new(userResponse)
|
|
||||||
require.NoError(t, json.Unmarshal(userResp.Body.Bytes(), userParsed))
|
|
||||||
assert.Contains(t, userParsed.Email, "user2@example.com")
|
|
||||||
|
|
||||||
errorReq := NewRequest(t, "GET", "/api/v1/users/user2/repos")
|
|
||||||
errorReq.SetHeader("Authorization", "Bearer "+parsed.AccessToken)
|
|
||||||
errorResp := MakeRequest(t, errorReq, http.StatusForbidden)
|
|
||||||
|
|
||||||
type errorResponse struct {
|
|
||||||
Message string `json:"message"`
|
|
||||||
}
|
|
||||||
|
|
||||||
errorParsed := new(errorResponse)
|
|
||||||
require.NoError(t, json.Unmarshal(errorResp.Body.Bytes(), errorParsed))
|
|
||||||
assert.Contains(t, errorParsed.Message, "token does not have at least one of required scope(s), required=[read:repository]")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func testOAuthGrantScopesReadRepositoryFailOrganization(t *testing.T) {
|
func testOAuthGrantScopesReadRepositoryFailOrganization(t *testing.T) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user