Blob Blame History Raw
From 74f3c59f7096b5c31d5c218310b20775eb111d0f Mon Sep 17 00:00:00 2001
From: Karl Persson <kalle.persson@grafana.com>
Date: Fri, 21 Oct 2022 14:15:21 +0200
Subject: [PATCH] [v9.0.x] Login email before username (#57406)

* Add test for username/login field conflict

* Swap order of login fields

Co-authored-by: linoman <2051016+linoman@users.noreply.github.com>

diff --git a/pkg/services/sqlstore/user.go b/pkg/services/sqlstore/user.go
index 9cd80da396..00e3ddc2df 100644
--- a/pkg/services/sqlstore/user.go
+++ b/pkg/services/sqlstore/user.go
@@ -170,20 +170,24 @@ func (ss *SQLStore) GetUserByLogin(ctx context.Context, query *models.GetUserByL
 			return models.ErrUserNotFound
 		}
 
-		// Try and find the user by login first.
-		// It's not sufficient to assume that a LoginOrEmail with an "@" is an email.
+		var has bool
+		var err error
 		user := &models.User{Login: query.LoginOrEmail}
-		has, err := sess.Where(notServiceAccountFilter(ss)).Get(user)
-
-		if err != nil {
-			return err
-		}
 
-		if !has && strings.Contains(query.LoginOrEmail, "@") {
-			// If the user wasn't found, and it contains an "@" fallback to finding the
-			// user by email.
+		// Since username can be an email address, attempt login with email address
+		// first if the login field has the "@" symbol.
+		if strings.Contains(query.LoginOrEmail, "@") {
 			user = &models.User{Email: query.LoginOrEmail}
 			has, err = sess.Get(user)
+
+			if err != nil {
+				return err
+			}
+		}
+
+		// Lookup the login field instead of email field
+		if !has {
+			has, err = sess.Where(notServiceAccountFilter(ss)).Get(user)
 		}
 
 		if err != nil {
diff --git a/pkg/services/sqlstore/user_test.go b/pkg/services/sqlstore/user_test.go
index d3803fa0c9..da23a7cca9 100644
--- a/pkg/services/sqlstore/user_test.go
+++ b/pkg/services/sqlstore/user_test.go
@@ -51,6 +51,45 @@ func TestIntegrationUserDataAccess(t *testing.T) {
 		require.False(t, query.Result.IsDisabled)
 	})
 
+	t.Run("Get User by login - user_2 uses user_1.email as login", func(t *testing.T) {
+		ss = InitTestDB(t)
+
+		// create user_1
+		cmd := models.CreateUserCommand{
+			Email:      "user_1@mail.com",
+			Name:       "user_1",
+			Login:      "user_1",
+			Password:   "user_1_password",
+			IsDisabled: true,
+		}
+		user_1, err := ss.CreateUser(context.Background(), cmd)
+		require.Nil(t, err)
+
+		// create user_2
+		cmd = models.CreateUserCommand{
+			Email:      "user_2@mail.com",
+			Name:       "user_2",
+			Login:      "user_1@mail.com",
+			Password:   "user_2_password",
+			IsDisabled: true,
+		}
+		user_2, err := ss.CreateUser(context.Background(), cmd)
+		require.Nil(t, err)
+
+		// query user database for user_1 email
+		query := models.GetUserByLoginQuery{LoginOrEmail: "user_1@mail.com"}
+		err = ss.GetUserByLogin(context.Background(), &query)
+		require.Nil(t, err)
+
+		// expect user_1 as result
+		require.Equal(t, user_1.Email, query.Result.Email)
+		require.Equal(t, user_1.Login, query.Result.Login)
+		require.Equal(t, user_1.Name, query.Result.Name)
+		require.NotEqual(t, user_2.Email, query.Result.Email)
+		require.NotEqual(t, user_2.Login, query.Result.Login)
+		require.NotEqual(t, user_2.Name, query.Result.Name)
+	})
+
 	t.Run("Testing DB - creates and loads disabled user", func(t *testing.T) {
 		ss = InitTestDB(t)
 		cmd := models.CreateUserCommand{