mirror of
https://github.com/go-gitea/gitea.git
synced 2026-11-01 07:40:21 +00:00
Backport https://github.com/go-gitea/gitea/pull/40121 With `[oauth2_client].ACCOUNT_LINKING = auto`, the link account sign-up form linked the external identity to an existing account matching the submitted user name or email and signed in as it. Automatic linking now only happens when the existing user matches the external identity, otherwise the linking login page asks for the account's password. `release/v28` lacks `TestOAuth2AvatarFromPicture`, so only the new test is added. Co-authored-by: bircni <bircni@icloud.com> Co-authored-by: Giteabot <teabot@gitea.io>
This commit is contained in:
2 files changed
+57
No files matched your search
@@ -447,6 +447,14 @@ func extractUserNameFromOAuth2(gothUser *goth.User) (string, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func oauth2IdentityMatchesUser(gothUser *goth.User, u *user_model.User) bool {
|
||||
if gothUser.Email != "" && strings.EqualFold(gothUser.Email, u.Email) {
|
||||
return true
|
||||
}
|
||||
uname, err := extractUserNameFromOAuth2(gothUser)
|
||||
return err == nil && uname != "" && strings.EqualFold(uname, u.Name)
|
||||
}
|
||||
|
||||
// HandleSignOut resets the session and sets the cookies
|
||||
func HandleSignOut(ctx *context.Context) {
|
||||
_ = ctx.Session.Flush()
|
||||
@@ -646,6 +654,9 @@ func createUserInContext(ctx *context.Context, tpl templates.TplName, form any,
|
||||
return false
|
||||
}
|
||||
}
|
||||
if !oauth2IdentityMatchesUser(&possibleLinkAccountData.GothUser, user) {
|
||||
break // name or email came from the sign-up form, report the conflict instead of linking
|
||||
}
|
||||
// TODO: probably we should respect 'remember' user's choice...
|
||||
oauth2LinkAccount(ctx, user, possibleLinkAccountData, true)
|
||||
return false // user is already created here, all redirects are handled
|
||||
|
||||
@@ -29,7 +29,10 @@ import (
|
||||
"gitea.dev/modules/test"
|
||||
"gitea.dev/modules/timeutil"
|
||||
"gitea.dev/modules/util"
|
||||
"gitea.dev/modules/web"
|
||||
"gitea.dev/routers/web/auth"
|
||||
"gitea.dev/services/auth/source/oauth2"
|
||||
"gitea.dev/services/context"
|
||||
"gitea.dev/services/oauth2_provider"
|
||||
"gitea.dev/tests"
|
||||
|
||||
@@ -41,6 +44,49 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestOAuth2LinkAccountSignupAutoLinking(t *testing.T) {
|
||||
defer tests.PrepareTestEnv(t)()
|
||||
defer test.MockVariableValue(&setting.OAuth2Client.AccountLinking, setting.OAuth2AccountLinkingAuto)()
|
||||
defer test.MockVariableValue(&setting.Service.RequireExternalRegistrationPassword, false)()
|
||||
|
||||
addOAuth2Source(t, "test-link-signup", oauth2.Source{})
|
||||
authSource, err := auth_model.GetActiveOAuth2SourceByAuthName(t.Context(), "test-link-signup")
|
||||
require.NoError(t, err)
|
||||
|
||||
signUp := func(t *testing.T, gothUser goth.User, userName, email string, expectedStatus int) (*TestSession, *httptest.ResponseRecorder) {
|
||||
defer web.RouteMockReset()
|
||||
web.RouteMock(web.MockAfterMiddlewares, func(ctx *context.Context) {
|
||||
require.NoError(t, auth.Oauth2SetLinkAccountData(ctx, auth.LinkAccountData{AuthSourceID: authSource.ID, GothUser: gothUser}))
|
||||
})
|
||||
session := emptyTestSession(t)
|
||||
req := NewRequestWithValues(t, "POST", "/user/link_account_signup", map[string]string{
|
||||
"user_name": userName,
|
||||
"email": email,
|
||||
})
|
||||
return session, session.MakeRequest(t, req, expectedStatus)
|
||||
}
|
||||
|
||||
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
for _, form := range []struct{ name, userName, email, errMsg string }{
|
||||
{"FormNamesOtherUser", user2.Name, "new-user@example.com", "The username is already taken."},
|
||||
{"FormUsesOtherUsersEmail", "new-user", user2.Email, "The email address is already used."},
|
||||
} {
|
||||
t.Run(form.name, func(t *testing.T) {
|
||||
gothUser := goth.User{UserID: form.name, Email: "external@example.com"}
|
||||
_, resp := signUp(t, gothUser, form.userName, form.email, http.StatusOK)
|
||||
assert.Contains(t, resp.Body.String(), form.errMsg)
|
||||
unittest.AssertNotExistsBean(t, &user_model.ExternalLoginUser{ExternalID: gothUser.UserID}, unittest.OrderBy("external_id"))
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("ExternalIdentityMatchesUser", func(t *testing.T) {
|
||||
gothUser := goth.User{UserID: "user2-sub", Email: user2.Email}
|
||||
session, _ := signUp(t, gothUser, user2.Name, user2.Email, http.StatusSeeOther)
|
||||
unittest.AssertExistsAndLoadBean(t, &user_model.ExternalLoginUser{ExternalID: gothUser.UserID, UserID: user2.ID}, unittest.OrderBy("external_id"))
|
||||
session.MakeRequest(t, NewRequest(t, "GET", "/user/settings"), http.StatusOK)
|
||||
})
|
||||
}
|
||||
|
||||
func testOAuth2PrepareTestCode(t *testing.T) {
|
||||
require.NoError(t, db.TruncateBeans(t.Context(), &auth_model.OAuth2AuthorizationCode{}))
|
||||
err := db.Insert(t.Context(), &auth_model.OAuth2AuthorizationCode{
|
||||
|
||||
Reference in new issue
Block a user