From b867ceda9b36be8b7d98f751b4b709abf011bcf4 Mon Sep 17 00:00:00 2001 From: Dimitris Sotirakis Date: Fri, 9 Apr 2021 14:28:35 +0300 Subject: [PATCH] Generic OAuth: Prevent adding duplicated users (#32286) * Add special check for generic oauth case * Converted from Convey to testify * Fix according to reviewer's comments * More changes according to reviewer's comments * Handle error if user is not found * Move generic oauth test from user_test.go to user_auth_test.go * Update pkg/services/sqlstore/user_auth_test.go Co-authored-by: Marcus Efraimsson * Created genericOAuthModule const Co-authored-by: Marcus Efraimsson --- pkg/services/sqlstore/user_auth.go | 15 +- pkg/services/sqlstore/user_auth_test.go | 135 +-- pkg/services/sqlstore/user_test.go | 1196 ++++++++++++----------- 3 files changed, 715 insertions(+), 631 deletions(-) diff --git a/pkg/services/sqlstore/user_auth.go b/pkg/services/sqlstore/user_auth.go index 0bef79e1600..7572b8990e5 100644 --- a/pkg/services/sqlstore/user_auth.go +++ b/pkg/services/sqlstore/user_auth.go @@ -13,6 +13,8 @@ import ( var getTime = time.Now +const genericOAuthModule = "oauth_generic_oauth" + func init() { bus.AddHandler("sql", GetUserByAuthInfo) bus.AddHandler("sql", GetExternalUserInfoByLogin) @@ -101,7 +103,17 @@ func GetUserByAuthInfo(query *models.GetUserByAuthInfoQuery) error { return models.ErrUserNotFound } - // create authInfo record to link accounts + // Special case for generic oauth duplicates + if query.AuthModule == genericOAuthModule && user.Id != 0 { + authQuery.UserId = user.Id + authQuery.AuthModule = query.AuthModule + err = GetAuthInfo(authQuery) + if !errors.Is(err, models.ErrUserNotFound) { + if err != nil { + return err + } + } + } if authQuery.Result == nil && query.AuthModule != "" { cmd2 := &models.SetAuthInfoCommand{ UserId: user.Id, @@ -151,6 +163,7 @@ func GetAuthInfo(query *models.GetAuthInfoQuery) error { if err != nil { return err } + if !has { return models.ErrUserNotFound } diff --git a/pkg/services/sqlstore/user_auth_test.go b/pkg/services/sqlstore/user_auth_test.go index 15f64decec3..4a3cc69e518 100644 --- a/pkg/services/sqlstore/user_auth_test.go +++ b/pkg/services/sqlstore/user_auth_test.go @@ -5,11 +5,11 @@ package sqlstore import ( "context" "fmt" + "github.com/stretchr/testify/require" "testing" "time" "github.com/grafana/grafana/pkg/models" - . "github.com/smartystreets/goconvey/convey" "golang.org/x/oauth2" ) @@ -17,7 +17,7 @@ import ( func TestUserAuth(t *testing.T) { sqlStore := InitTestDB(t) - Convey("Given 5 users", t, func() { + t.Run("Given 5 users", func(t *testing.T) { for i := 0; i < 5; i++ { cmd := models.CreateUserCommand{ Email: fmt.Sprint("user", i, "@test.com"), @@ -25,29 +25,18 @@ func TestUserAuth(t *testing.T) { Login: fmt.Sprint("loginuser", i), } _, err := sqlStore.CreateUser(context.Background(), cmd) - So(err, ShouldBeNil) + require.Nil(t, err) } - Reset(func() { - _, err := x.Exec("DELETE FROM org_user WHERE 1=1") - So(err, ShouldBeNil) - _, err = x.Exec("DELETE FROM org WHERE 1=1") - So(err, ShouldBeNil) - _, err = x.Exec("DELETE FROM " + dialect.Quote("user") + " WHERE 1=1") - So(err, ShouldBeNil) - _, err = x.Exec("DELETE FROM user_auth WHERE 1=1") - So(err, ShouldBeNil) - }) - - Convey("Can find existing user", func() { + t.Run("Can find existing user", func(t *testing.T) { // By Login login := "loginuser0" query := &models.GetUserByAuthInfoQuery{Login: login} err := GetUserByAuthInfo(query) - So(err, ShouldBeNil) - So(query.Result.Login, ShouldEqual, login) + require.Nil(t, err) + require.Equal(t, query.Result.Login, login) // By ID id := query.Result.Id @@ -55,8 +44,8 @@ func TestUserAuth(t *testing.T) { query = &models.GetUserByAuthInfoQuery{UserId: id} err = GetUserByAuthInfo(query) - So(err, ShouldBeNil) - So(query.Result.Id, ShouldEqual, id) + require.Nil(t, err) + require.Equal(t, query.Result.Id, id) // By Email email := "user1@test.com" @@ -64,8 +53,8 @@ func TestUserAuth(t *testing.T) { query = &models.GetUserByAuthInfoQuery{Email: email} err = GetUserByAuthInfo(query) - So(err, ShouldBeNil) - So(query.Result.Email, ShouldEqual, email) + require.Nil(t, err) + require.Equal(t, query.Result.Email, email) // Don't find nonexistent user email = "nonexistent@test.com" @@ -73,17 +62,17 @@ func TestUserAuth(t *testing.T) { query = &models.GetUserByAuthInfoQuery{Email: email} err = GetUserByAuthInfo(query) - So(err, ShouldEqual, models.ErrUserNotFound) - So(query.Result, ShouldBeNil) + require.Equal(t, err, models.ErrUserNotFound) + require.Nil(t, query.Result) }) - Convey("Can set & locate by AuthModule and AuthId", func() { + t.Run("Can set & locate by AuthModule and AuthId", func(t *testing.T) { // get nonexistent user_auth entry query := &models.GetUserByAuthInfoQuery{AuthModule: "test", AuthId: "test"} err := GetUserByAuthInfo(query) - So(err, ShouldEqual, models.ErrUserNotFound) - So(query.Result, ShouldBeNil) + require.Equal(t, err, models.ErrUserNotFound) + require.Nil(t, query.Result) // create user_auth entry login := "loginuser0" @@ -91,15 +80,15 @@ func TestUserAuth(t *testing.T) { query.Login = login err = GetUserByAuthInfo(query) - So(err, ShouldBeNil) - So(query.Result.Login, ShouldEqual, login) + require.Nil(t, err) + require.Equal(t, query.Result.Login, login) // get via user_auth query = &models.GetUserByAuthInfoQuery{AuthModule: "test", AuthId: "test"} err = GetUserByAuthInfo(query) - So(err, ShouldBeNil) - So(query.Result.Login, ShouldEqual, login) + require.Nil(t, err) + require.Equal(t, query.Result.Login, login) // get with non-matching id id := query.Result.Id @@ -107,29 +96,29 @@ func TestUserAuth(t *testing.T) { query.UserId = id + 1 err = GetUserByAuthInfo(query) - So(err, ShouldBeNil) - So(query.Result.Login, ShouldEqual, "loginuser1") + require.Nil(t, err) + require.Equal(t, query.Result.Login, "loginuser1") // get via user_auth query = &models.GetUserByAuthInfoQuery{AuthModule: "test", AuthId: "test"} err = GetUserByAuthInfo(query) - So(err, ShouldBeNil) - So(query.Result.Login, ShouldEqual, "loginuser1") + require.Nil(t, err) + require.Equal(t, query.Result.Login, "loginuser1") // remove user _, err = x.Exec("DELETE FROM "+dialect.Quote("user")+" WHERE id=?", query.Result.Id) - So(err, ShouldBeNil) + require.Nil(t, err) // get via user_auth for deleted user query = &models.GetUserByAuthInfoQuery{AuthModule: "test", AuthId: "test"} err = GetUserByAuthInfo(query) - So(err, ShouldEqual, models.ErrUserNotFound) - So(query.Result, ShouldBeNil) + require.Equal(t, err, models.ErrUserNotFound) + require.Nil(t, query.Result) }) - Convey("Can set & retrieve oauth token information", func() { + t.Run("Can set & retrieve oauth token information", func(t *testing.T) { token := &oauth2.Token{ AccessToken: "testaccess", RefreshToken: "testrefresh", @@ -144,8 +133,8 @@ func TestUserAuth(t *testing.T) { query := &models.GetUserByAuthInfoQuery{Login: login, AuthModule: "test", AuthId: "test"} err := GetUserByAuthInfo(query) - So(err, ShouldBeNil) - So(query.Result.Login, ShouldEqual, login) + require.Nil(t, err) + require.Equal(t, query.Result.Login, login) cmd := &models.UpdateAuthInfoCommand{ UserId: query.Result.Id, @@ -155,7 +144,7 @@ func TestUserAuth(t *testing.T) { } err = UpdateAuthInfo(cmd) - So(err, ShouldBeNil) + require.Nil(t, err) getAuthQuery := &models.GetAuthInfoQuery{ UserId: query.Result.Id, @@ -163,13 +152,26 @@ func TestUserAuth(t *testing.T) { err = GetAuthInfo(getAuthQuery) - So(err, ShouldBeNil) - So(getAuthQuery.Result.OAuthAccessToken, ShouldEqual, token.AccessToken) - So(getAuthQuery.Result.OAuthRefreshToken, ShouldEqual, token.RefreshToken) - So(getAuthQuery.Result.OAuthTokenType, ShouldEqual, token.TokenType) + require.Nil(t, err) + require.Equal(t, getAuthQuery.Result.OAuthAccessToken, token.AccessToken) + require.Equal(t, getAuthQuery.Result.OAuthRefreshToken, token.RefreshToken) + require.Equal(t, getAuthQuery.Result.OAuthTokenType, token.TokenType) }) - Convey("Always return the most recently used auth_module", func() { + t.Run("Always return the most recently used auth_module", func(t *testing.T) { + // Restore after destructive operation + sqlStore = InitTestDB(t) + + for i := 0; i < 5; i++ { + cmd := models.CreateUserCommand{ + Email: fmt.Sprint("user", i, "@test.com"), + Name: fmt.Sprint("user", i), + Login: fmt.Sprint("loginuser", i), + } + _, err := sqlStore.CreateUser(context.Background(), cmd) + require.Nil(t, err) + } + // Find a user to set tokens on login := "loginuser0" @@ -180,8 +182,8 @@ func TestUserAuth(t *testing.T) { err := GetUserByAuthInfo(query) getTime = time.Now - So(err, ShouldBeNil) - So(query.Result.Login, ShouldEqual, login) + require.Nil(t, err) + require.Equal(t, query.Result.Login, login) // Add a second auth module for this user // Have this module's last log-in be more recent @@ -190,8 +192,8 @@ func TestUserAuth(t *testing.T) { err = GetUserByAuthInfo(query) getTime = time.Now - So(err, ShouldBeNil) - So(query.Result.Login, ShouldEqual, login) + require.Nil(t, err) + require.Equal(t, query.Result.Login, login) // Get the latest entry by not supply an authmodule or authid getAuthQuery := &models.GetAuthInfoQuery{ @@ -200,14 +202,14 @@ func TestUserAuth(t *testing.T) { err = GetAuthInfo(getAuthQuery) - So(err, ShouldBeNil) - So(getAuthQuery.Result.AuthModule, ShouldEqual, "test2") + require.Nil(t, err) + require.Equal(t, getAuthQuery.Result.AuthModule, "test2") // "log in" again with the first auth module updateAuthCmd := &models.UpdateAuthInfoCommand{UserId: query.Result.Id, AuthModule: "test1", AuthId: "test1"} err = UpdateAuthInfo(updateAuthCmd) - So(err, ShouldBeNil) + require.Nil(t, err) // Get the latest entry by not supply an authmodule or authid getAuthQuery = &models.GetAuthInfoQuery{ @@ -216,8 +218,31 @@ func TestUserAuth(t *testing.T) { err = GetAuthInfo(getAuthQuery) - So(err, ShouldBeNil) - So(getAuthQuery.Result.AuthModule, ShouldEqual, "test1") + require.Nil(t, err) + require.Equal(t, getAuthQuery.Result.AuthModule, "test1") + }) + + t.Run("Can set & locate by generic oauth auth module and user id", func(t *testing.T) { + // Find a user to set tokens on + login := "loginuser0" + + // Expect to pass since there's a matching login user + getTime = func() time.Time { return time.Now().AddDate(0, 0, -2) } + query := &models.GetUserByAuthInfoQuery{Login: login, AuthModule: genericOAuthModule, AuthId: ""} + err := GetUserByAuthInfo(query) + getTime = time.Now + + require.Nil(t, err) + require.Equal(t, query.Result.Login, login) + + // Should throw a "user not found" error since there's no matching login user + getTime = func() time.Time { return time.Now().AddDate(0, 0, -2) } + query = &models.GetUserByAuthInfoQuery{Login: "aloginuser", AuthModule: genericOAuthModule, AuthId: ""} + err = GetUserByAuthInfo(query) + getTime = time.Now + + require.NotNil(t, err) + require.Nil(t, query.Result) }) }) } diff --git a/pkg/services/sqlstore/user_test.go b/pkg/services/sqlstore/user_test.go index ae93403bbb1..58f7b418110 100644 --- a/pkg/services/sqlstore/user_test.go +++ b/pkg/services/sqlstore/user_test.go @@ -8,599 +8,645 @@ import ( "testing" "time" - "github.com/grafana/grafana/pkg/setting" - - . "github.com/smartystreets/goconvey/convey" - "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/setting" + "github.com/stretchr/testify/require" ) func TestUserDataAccess(t *testing.T) { - Convey("Testing DB", t, func() { - ss := InitTestDB(t) - Convey("Creates a user", func() { - cmd := models.CreateUserCommand{ - Email: "usertest@test.com", - Name: "user name", - Login: "user_test_login", + ss := InitTestDB(t) + + t.Run("Testing DB - creates and loads user", func(t *testing.T) { + cmd := models.CreateUserCommand{ + Email: "usertest@test.com", + Name: "user name", + Login: "user_test_login", + } + user, err := ss.CreateUser(context.Background(), cmd) + require.NoError(t, err) + + query := models.GetUserByIdQuery{Id: user.Id} + err = GetUserById(&query) + require.Nil(t, err) + + require.Equal(t, query.Result.Email, "usertest@test.com") + require.Equal(t, query.Result.Password, "") + require.Len(t, query.Result.Rands, 10) + require.Len(t, query.Result.Salt, 10) + require.False(t, query.Result.IsDisabled) + + query = models.GetUserByIdQuery{Id: user.Id} + err = GetUserById(&query) + require.Nil(t, err) + + require.Equal(t, query.Result.Email, "usertest@test.com") + require.Equal(t, query.Result.Password, "") + require.Len(t, query.Result.Rands, 10) + require.Len(t, query.Result.Salt, 10) + require.False(t, query.Result.IsDisabled) + }) + + t.Run("Testing DB - creates and loads disabled user", func(t *testing.T) { + ss = InitTestDB(t) + cmd := models.CreateUserCommand{ + Email: "usertest@test.com", + Name: "user name", + Login: "user_test_login", + IsDisabled: true, + } + + user, err := ss.CreateUser(context.Background(), cmd) + require.Nil(t, err) + + query := models.GetUserByIdQuery{Id: user.Id} + err = GetUserById(&query) + require.Nil(t, err) + + require.Equal(t, query.Result.Email, "usertest@test.com") + require.Equal(t, query.Result.Password, "") + require.Len(t, query.Result.Rands, 10) + require.Len(t, query.Result.Salt, 10) + require.True(t, query.Result.IsDisabled) + }) + + t.Run("Testing DB - create user assigned to other organization", func(t *testing.T) { + ss = InitTestDB(t) + + autoAssignOrg := setting.AutoAssignOrg + setting.AutoAssignOrg = true + defer func() { + setting.AutoAssignOrg = autoAssignOrg + }() + + orgCmd := &models.CreateOrgCommand{Name: "Some Test Org"} + err := CreateOrg(orgCmd) + require.Nil(t, err) + + cmd := models.CreateUserCommand{ + Email: "usertest@test.com", + Name: "user name", + Login: "user_test_login", + OrgId: orgCmd.Result.Id, + } + + user, err := ss.CreateUser(context.Background(), cmd) + require.Nil(t, err) + + query := models.GetUserByIdQuery{Id: user.Id} + err = GetUserById(&query) + require.Nil(t, err) + + require.Equal(t, query.Result.Email, "usertest@test.com") + require.Equal(t, query.Result.Password, "") + require.Len(t, query.Result.Rands, 10) + require.Len(t, query.Result.Salt, 10) + require.False(t, query.Result.IsDisabled) + require.Equal(t, query.Result.OrgId, orgCmd.Result.Id) + + const nonExistingOrgID = 10000 + cmd = models.CreateUserCommand{ + Email: "usertest@test.com", + Name: "user name", + Login: "user_test_login", + OrgId: nonExistingOrgID, + } + + _, err = ss.CreateUser(context.Background(), cmd) + require.Equal(t, err, models.ErrOrgNotFound) + }) + + t.Run("Testing DB - multiple users", func(t *testing.T) { + ss = InitTestDB(t) + + users := createFiveTestUsers(t, ss, func(i int) *models.CreateUserCommand { + return &models.CreateUserCommand{ + Email: fmt.Sprint("user", i, "@test.com"), + Name: fmt.Sprint("user", i), + Login: fmt.Sprint("loginuser", i), + IsDisabled: false, } - - user, err := ss.CreateUser(context.Background(), cmd) - So(err, ShouldBeNil) - - Convey("Loading a user", func() { - query := models.GetUserByIdQuery{Id: user.Id} - err := GetUserById(&query) - So(err, ShouldBeNil) - - So(query.Result.Email, ShouldEqual, "usertest@test.com") - So(query.Result.Password, ShouldEqual, "") - So(query.Result.Rands, ShouldHaveLength, 10) - So(query.Result.Salt, ShouldHaveLength, 10) - So(query.Result.IsDisabled, ShouldBeFalse) - }) }) - Convey("Creates disabled user", func() { - cmd := models.CreateUserCommand{ - Email: "usertest@test.com", - Name: "user name", - Login: "user_test_login", + // Return the first page of users and a total count + query := models.SearchUsersQuery{Query: "", Page: 1, Limit: 3} + err := SearchUsers(&query) + + require.Nil(t, err) + require.Len(t, query.Result.Users, 3) + require.EqualValues(t, query.Result.TotalCount, 5) + + // Return the second page of users and a total count + query = models.SearchUsersQuery{Query: "", Page: 2, Limit: 3} + err = SearchUsers(&query) + + require.Nil(t, err) + require.Len(t, query.Result.Users, 2) + require.EqualValues(t, query.Result.TotalCount, 5) + + // Return list of users matching query on user name + query = models.SearchUsersQuery{Query: "use", Page: 1, Limit: 3} + err = SearchUsers(&query) + + require.Nil(t, err) + require.Len(t, query.Result.Users, 3) + require.EqualValues(t, query.Result.TotalCount, 5) + + query = models.SearchUsersQuery{Query: "ser1", Page: 1, Limit: 3} + err = SearchUsers(&query) + + require.Nil(t, err) + require.Len(t, query.Result.Users, 1) + require.EqualValues(t, query.Result.TotalCount, 1) + + query = models.SearchUsersQuery{Query: "USER1", Page: 1, Limit: 3} + err = SearchUsers(&query) + + require.Nil(t, err) + require.Len(t, query.Result.Users, 1) + require.EqualValues(t, query.Result.TotalCount, 1) + + query = models.SearchUsersQuery{Query: "idontexist", Page: 1, Limit: 3} + err = SearchUsers(&query) + + require.Nil(t, err) + require.Len(t, query.Result.Users, 0) + require.EqualValues(t, query.Result.TotalCount, 0) + + // Return list of users matching query on email + query = models.SearchUsersQuery{Query: "ser1@test.com", Page: 1, Limit: 3} + err = SearchUsers(&query) + + require.Nil(t, err) + require.Len(t, query.Result.Users, 1) + require.EqualValues(t, query.Result.TotalCount, 1) + + // Return list of users matching query on login name + query = models.SearchUsersQuery{Query: "loginuser1", Page: 1, Limit: 3} + err = SearchUsers(&query) + + require.Nil(t, err) + require.Len(t, query.Result.Users, 1) + require.EqualValues(t, query.Result.TotalCount, 1) + + // Return list users based on their auth type + for index, user := range users { + authModule := "killa" + + // define every second user as ldap + if index%2 == 0 { + authModule = "ldap" + } + + cmd2 := &models.SetAuthInfoCommand{ + UserId: user.Id, + AuthModule: authModule, + AuthId: "gorilla", + } + err := SetAuthInfo(cmd2) + require.Nil(t, err) + } + query = models.SearchUsersQuery{AuthModule: "ldap"} + err = SearchUsers(&query) + require.Nil(t, err) + + require.Len(t, query.Result.Users, 3) + + zero, second, fourth := false, false, false + for _, user := range query.Result.Users { + if user.Name == "user0" { + zero = true + } + + if user.Name == "user2" { + second = true + } + + if user.Name == "user4" { + fourth = true + } + } + + require.True(t, zero) + require.True(t, second) + require.True(t, fourth) + }) + + t.Run("Testing DB - return list users based on their is_disabled flag", func(t *testing.T) { + ss = InitTestDB(t) + createFiveTestUsers(t, ss, func(i int) *models.CreateUserCommand { + return &models.CreateUserCommand{ + Email: fmt.Sprint("user", i, "@test.com"), + Name: fmt.Sprint("user", i), + Login: fmt.Sprint("loginuser", i), + IsDisabled: i%2 == 0, + } + }) + + isDisabled := false + query := models.SearchUsersQuery{IsDisabled: &isDisabled} + err := SearchUsers(&query) + require.Nil(t, err) + + require.Len(t, query.Result.Users, 2) + + first, third := false, false + for _, user := range query.Result.Users { + if user.Name == "user1" { + first = true + } + + if user.Name == "user3" { + third = true + } + } + + require.True(t, first) + require.True(t, third) + + // Re-init DB + ss = InitTestDB(t) + users := createFiveTestUsers(t, ss, func(i int) *models.CreateUserCommand { + return &models.CreateUserCommand{ + Email: fmt.Sprint("user", i, "@test.com"), + Name: fmt.Sprint("user", i), + Login: fmt.Sprint("loginuser", i), + IsDisabled: false, + } + }) + + err = AddOrgUser(&models.AddOrgUserCommand{ + LoginOrEmail: users[1].Login, Role: models.ROLE_VIEWER, + OrgId: users[0].OrgId, UserId: users[1].Id, + }) + require.Nil(t, err) + + err = testHelperUpdateDashboardAcl(t, ss, 1, models.DashboardAcl{ + DashboardID: 1, OrgID: users[0].OrgId, UserID: users[1].Id, + Permission: models.PERMISSION_EDIT, + }) + require.Nil(t, err) + + err = SavePreferences(&models.SavePreferencesCommand{ + UserId: users[1].Id, OrgId: users[0].OrgId, HomeDashboardId: 1, Theme: "dark", + }) + require.Nil(t, err) + + // When the user is deleted + err = DeleteUser(&models.DeleteUserCommand{UserId: users[1].Id}) + require.Nil(t, err) + + query1 := &models.GetOrgUsersQuery{OrgId: users[0].OrgId} + err = GetOrgUsersForTest(query1) + require.Nil(t, err) + + require.Len(t, query1.Result, 1) + + permQuery := &models.GetDashboardAclInfoListQuery{DashboardID: 1, OrgID: users[0].OrgId} + err = GetDashboardAclInfoList(permQuery) + require.Nil(t, err) + + require.Len(t, permQuery.Result, 0) + + prefsQuery := &models.GetPreferencesQuery{OrgId: users[0].OrgId, UserId: users[1].Id} + err = GetPreferences(prefsQuery) + require.Nil(t, err) + + require.EqualValues(t, prefsQuery.Result.OrgId, 0) + require.EqualValues(t, prefsQuery.Result.UserId, 0) + + // A user is an org member and has been assigned permissions + // Re-init DB + ss = InitTestDB(t) + users = createFiveTestUsers(t, ss, func(i int) *models.CreateUserCommand { + return &models.CreateUserCommand{ + Email: fmt.Sprint("user", i, "@test.com"), + Name: fmt.Sprint("user", i), + Login: fmt.Sprint("loginuser", i), + IsDisabled: false, + } + }) + err = AddOrgUser(&models.AddOrgUserCommand{ + LoginOrEmail: users[1].Login, Role: models.ROLE_VIEWER, + OrgId: users[0].OrgId, UserId: users[1].Id, + }) + require.Nil(t, err) + + err = testHelperUpdateDashboardAcl(t, ss, 1, models.DashboardAcl{ + DashboardID: 1, OrgID: users[0].OrgId, UserID: users[1].Id, + Permission: models.PERMISSION_EDIT, + }) + require.Nil(t, err) + + err = SavePreferences(&models.SavePreferencesCommand{ + UserId: users[1].Id, OrgId: users[0].OrgId, HomeDashboardId: 1, Theme: "dark", + }) + require.Nil(t, err) + + ss.CacheService.Flush() + + query3 := &models.GetSignedInUserQuery{OrgId: users[1].OrgId, UserId: users[1].Id} + err = ss.GetSignedInUserWithCache(query3) + require.Nil(t, err) + require.NotNil(t, query3.Result) + require.Equal(t, query3.OrgId, users[1].OrgId) + err = SetUsingOrg(&models.SetUsingOrgCommand{UserId: users[1].Id, OrgId: users[0].OrgId}) + require.Nil(t, err) + query4 := &models.GetSignedInUserQuery{OrgId: 0, UserId: users[1].Id} + err = ss.GetSignedInUserWithCache(query4) + require.Nil(t, err) + require.NotNil(t, query4.Result) + require.Equal(t, query4.Result.OrgId, users[0].OrgId) + + cacheKey := newSignedInUserCacheKey(query4.Result.OrgId, query4.UserId) + _, found := ss.CacheService.Get(cacheKey) + require.True(t, found) + + disableCmd := models.BatchDisableUsersCommand{ + UserIds: []int64{1, 2, 3, 4, 5}, + IsDisabled: true, + } + + err = BatchDisableUsers(&disableCmd) + require.Nil(t, err) + + isDisabled = true + query5 := &models.SearchUsersQuery{IsDisabled: &isDisabled} + err = SearchUsers(query5) + + require.Nil(t, err) + require.EqualValues(t, query5.Result.TotalCount, 5) + + // the user is deleted + err = DeleteUser(&models.DeleteUserCommand{UserId: users[1].Id}) + require.Nil(t, err) + + // delete connected org users and permissions + query2 := &models.GetOrgUsersQuery{OrgId: users[0].OrgId} + err = GetOrgUsersForTest(query2) + require.Nil(t, err) + + require.Len(t, query2.Result, 1) + + permQuery = &models.GetDashboardAclInfoListQuery{DashboardID: 1, OrgID: users[0].OrgId} + err = GetDashboardAclInfoList(permQuery) + require.Nil(t, err) + + require.Len(t, permQuery.Result, 0) + + prefsQuery = &models.GetPreferencesQuery{OrgId: users[0].OrgId, UserId: users[1].Id} + err = GetPreferences(prefsQuery) + require.Nil(t, err) + + require.EqualValues(t, prefsQuery.Result.OrgId, 0) + require.EqualValues(t, prefsQuery.Result.UserId, 0) + }) + + ss = InitTestDB(t) + + t.Run("Testing DB - enable all users", func(t *testing.T) { + + createFiveTestUsers(t, ss, func(i int) *models.CreateUserCommand { + return &models.CreateUserCommand{ + Email: fmt.Sprint("user", i, "@test.com"), + Name: fmt.Sprint("user", i), + Login: fmt.Sprint("loginuser", i), IsDisabled: true, } - - user, err := ss.CreateUser(context.Background(), cmd) - So(err, ShouldBeNil) - - Convey("Loading a user", func() { - query := models.GetUserByIdQuery{Id: user.Id} - err := GetUserById(&query) - So(err, ShouldBeNil) - - So(query.Result.Email, ShouldEqual, "usertest@test.com") - So(query.Result.Password, ShouldEqual, "") - So(query.Result.Rands, ShouldHaveLength, 10) - So(query.Result.Salt, ShouldHaveLength, 10) - So(query.Result.IsDisabled, ShouldBeTrue) - }) }) - Convey("Given an organization", func() { - autoAssignOrg := setting.AutoAssignOrg - setting.AutoAssignOrg = true - defer func() { - setting.AutoAssignOrg = autoAssignOrg - }() - - orgCmd := &models.CreateOrgCommand{Name: "Some Test Org"} - err := CreateOrg(orgCmd) - So(err, ShouldBeNil) - - Convey("Creates user assigned to other organization", func() { - cmd := models.CreateUserCommand{ - Email: "usertest@test.com", - Name: "user name", - Login: "user_test_login", - OrgId: orgCmd.Result.Id, - } - - user, err := ss.CreateUser(context.Background(), cmd) - So(err, ShouldBeNil) - - Convey("Loading a user", func() { - query := models.GetUserByIdQuery{Id: user.Id} - err := GetUserById(&query) - So(err, ShouldBeNil) - - So(query.Result.Email, ShouldEqual, "usertest@test.com") - So(query.Result.Password, ShouldEqual, "") - So(query.Result.Rands, ShouldHaveLength, 10) - So(query.Result.Salt, ShouldHaveLength, 10) - So(query.Result.IsDisabled, ShouldBeFalse) - So(query.Result.OrgId, ShouldEqual, orgCmd.Result.Id) - }) - }) - - Convey("Don't create user assigned to unknown organization", func() { - const nonExistingOrgID = 10000 - cmd := models.CreateUserCommand{ - Email: "usertest@test.com", - Name: "user name", - Login: "user_test_login", - OrgId: nonExistingOrgID, - } - - _, err := ss.CreateUser(context.Background(), cmd) - So(err, ShouldEqual, models.ErrOrgNotFound) - }) - }) - - Convey("Given 5 users", func() { - users := createFiveTestUsers(t, ss, func(i int) *models.CreateUserCommand { - return &models.CreateUserCommand{ - Email: fmt.Sprint("user", i, "@test.com"), - Name: fmt.Sprint("user", i), - Login: fmt.Sprint("loginuser", i), - IsDisabled: false, - } - }) - - Convey("Can return the first page of users and a total count", func() { - query := models.SearchUsersQuery{Query: "", Page: 1, Limit: 3} - err := SearchUsers(&query) - - So(err, ShouldBeNil) - So(len(query.Result.Users), ShouldEqual, 3) - So(query.Result.TotalCount, ShouldEqual, 5) - }) - - Convey("Can return the second page of users and a total count", func() { - query := models.SearchUsersQuery{Query: "", Page: 2, Limit: 3} - err := SearchUsers(&query) - - So(err, ShouldBeNil) - So(len(query.Result.Users), ShouldEqual, 2) - So(query.Result.TotalCount, ShouldEqual, 5) - }) - - Convey("Can return list of users matching query on user name", func() { - query := models.SearchUsersQuery{Query: "use", Page: 1, Limit: 3} - err := SearchUsers(&query) - - So(err, ShouldBeNil) - So(len(query.Result.Users), ShouldEqual, 3) - So(query.Result.TotalCount, ShouldEqual, 5) - - query = models.SearchUsersQuery{Query: "ser1", Page: 1, Limit: 3} - err = SearchUsers(&query) - - So(err, ShouldBeNil) - So(len(query.Result.Users), ShouldEqual, 1) - So(query.Result.TotalCount, ShouldEqual, 1) - - query = models.SearchUsersQuery{Query: "USER1", Page: 1, Limit: 3} - err = SearchUsers(&query) - - So(err, ShouldBeNil) - So(len(query.Result.Users), ShouldEqual, 1) - So(query.Result.TotalCount, ShouldEqual, 1) - - query = models.SearchUsersQuery{Query: "idontexist", Page: 1, Limit: 3} - err = SearchUsers(&query) - - So(err, ShouldBeNil) - So(len(query.Result.Users), ShouldEqual, 0) - So(query.Result.TotalCount, ShouldEqual, 0) - }) - - Convey("Can return list of users matching query on email", func() { - query := models.SearchUsersQuery{Query: "ser1@test.com", Page: 1, Limit: 3} - err := SearchUsers(&query) - - So(err, ShouldBeNil) - So(len(query.Result.Users), ShouldEqual, 1) - So(query.Result.TotalCount, ShouldEqual, 1) - }) - - Convey("Can return list of users matching query on login name", func() { - query := models.SearchUsersQuery{Query: "loginuser1", Page: 1, Limit: 3} - err := SearchUsers(&query) - - So(err, ShouldBeNil) - So(len(query.Result.Users), ShouldEqual, 1) - So(query.Result.TotalCount, ShouldEqual, 1) - }) - - Convey("Can return list users based on their auth type", func() { - // add users to auth table - for index, user := range users { - authModule := "killa" - - // define every second user as ldap - if index%2 == 0 { - authModule = "ldap" - } - - cmd2 := &models.SetAuthInfoCommand{ - UserId: user.Id, - AuthModule: authModule, - AuthId: "gorilla", - } - err := SetAuthInfo(cmd2) - So(err, ShouldBeNil) - } - query := models.SearchUsersQuery{AuthModule: "ldap"} - err := SearchUsers(&query) - So(err, ShouldBeNil) - - So(query.Result.Users, ShouldHaveLength, 3) - - zero, second, fourth := false, false, false - for _, user := range query.Result.Users { - if user.Name == "user0" { - zero = true - } - - if user.Name == "user2" { - second = true - } - - if user.Name == "user4" { - fourth = true - } - } - - So(zero, ShouldBeTrue) - So(second, ShouldBeTrue) - So(fourth, ShouldBeTrue) - }) - - Convey("Can return list users based on their is_disabled flag", func() { - ss = InitTestDB(t) - createFiveTestUsers(t, ss, func(i int) *models.CreateUserCommand { - return &models.CreateUserCommand{ - Email: fmt.Sprint("user", i, "@test.com"), - Name: fmt.Sprint("user", i), - Login: fmt.Sprint("loginuser", i), - IsDisabled: i%2 == 0, - } - }) - - isDisabled := false - query := models.SearchUsersQuery{IsDisabled: &isDisabled} - err := SearchUsers(&query) - So(err, ShouldBeNil) - - So(query.Result.Users, ShouldHaveLength, 2) - - first, third := false, false - for _, user := range query.Result.Users { - if user.Name == "user1" { - first = true - } - - if user.Name == "user3" { - third = true - } - } - - So(first, ShouldBeTrue) - So(third, ShouldBeTrue) - - ss = InitTestDB(t) - users = createFiveTestUsers(t, ss, func(i int) *models.CreateUserCommand { - return &models.CreateUserCommand{ - Email: fmt.Sprint("user", i, "@test.com"), - Name: fmt.Sprint("user", i), - Login: fmt.Sprint("loginuser", i), - IsDisabled: false, - } - }) - }) - - Convey("when a user is an org member and has been assigned permissions", func() { - err := AddOrgUser(&models.AddOrgUserCommand{ - LoginOrEmail: users[1].Login, Role: models.ROLE_VIEWER, - OrgId: users[0].OrgId, UserId: users[1].Id, - }) - So(err, ShouldBeNil) - - err = testHelperUpdateDashboardAcl(t, ss, 1, models.DashboardAcl{ - DashboardID: 1, OrgID: users[0].OrgId, UserID: users[1].Id, - Permission: models.PERMISSION_EDIT, - }) - So(err, ShouldBeNil) - - err = SavePreferences(&models.SavePreferencesCommand{ - UserId: users[1].Id, OrgId: users[0].OrgId, HomeDashboardId: 1, Theme: "dark", - }) - So(err, ShouldBeNil) - - Convey("when the user is deleted", func() { - err = DeleteUser(&models.DeleteUserCommand{UserId: users[1].Id}) - So(err, ShouldBeNil) - - Convey("Should delete connected org users and permissions", func() { - query := &models.GetOrgUsersQuery{OrgId: users[0].OrgId} - err = GetOrgUsersForTest(query) - So(err, ShouldBeNil) - - So(len(query.Result), ShouldEqual, 1) - - permQuery := &models.GetDashboardAclInfoListQuery{DashboardID: 1, OrgID: users[0].OrgId} - err = GetDashboardAclInfoList(permQuery) - So(err, ShouldBeNil) - - So(len(permQuery.Result), ShouldEqual, 0) - - prefsQuery := &models.GetPreferencesQuery{OrgId: users[0].OrgId, UserId: users[1].Id} - err = GetPreferences(prefsQuery) - So(err, ShouldBeNil) - - So(prefsQuery.Result.OrgId, ShouldEqual, 0) - So(prefsQuery.Result.UserId, ShouldEqual, 0) - }) - }) - - Convey("when retrieving signed in user for orgId=0 result should return active org id", func() { - ss.CacheService.Flush() - - query := &models.GetSignedInUserQuery{OrgId: users[1].OrgId, UserId: users[1].Id} - err := ss.GetSignedInUserWithCache(query) - So(err, ShouldBeNil) - So(query.Result, ShouldNotBeNil) - So(query.OrgId, ShouldEqual, users[1].OrgId) - err = SetUsingOrg(&models.SetUsingOrgCommand{UserId: users[1].Id, OrgId: users[0].OrgId}) - So(err, ShouldBeNil) - query = &models.GetSignedInUserQuery{OrgId: 0, UserId: users[1].Id} - err = ss.GetSignedInUserWithCache(query) - So(err, ShouldBeNil) - So(query.Result, ShouldNotBeNil) - So(query.Result.OrgId, ShouldEqual, users[0].OrgId) - - cacheKey := newSignedInUserCacheKey(query.Result.OrgId, query.UserId) - _, found := ss.CacheService.Get(cacheKey) - So(found, ShouldBeTrue) - }) - }) - - Convey("When batch disabling users", func() { - Convey("Should disable all users", func() { - disableCmd := models.BatchDisableUsersCommand{ - UserIds: []int64{1, 2, 3, 4, 5}, - IsDisabled: true, - } - - err := BatchDisableUsers(&disableCmd) - So(err, ShouldBeNil) - - isDisabled := true - query := &models.SearchUsersQuery{IsDisabled: &isDisabled} - err = SearchUsers(query) - - So(err, ShouldBeNil) - So(query.Result.TotalCount, ShouldEqual, 5) - }) - - Convey("Should enable all users", func() { - ss = InitTestDB(t) - createFiveTestUsers(t, ss, func(i int) *models.CreateUserCommand { - return &models.CreateUserCommand{ - Email: fmt.Sprint("user", i, "@test.com"), - Name: fmt.Sprint("user", i), - Login: fmt.Sprint("loginuser", i), - IsDisabled: true, - } - }) - - disableCmd := models.BatchDisableUsersCommand{ - UserIds: []int64{1, 2, 3, 4, 5}, - IsDisabled: false, - } - - err := BatchDisableUsers(&disableCmd) - So(err, ShouldBeNil) - - isDisabled := false - query := &models.SearchUsersQuery{IsDisabled: &isDisabled} - err = SearchUsers(query) - - So(err, ShouldBeNil) - So(query.Result.TotalCount, ShouldEqual, 5) - }) - - Convey("Should disable only specific users", func() { - ss = InitTestDB(t) - users = createFiveTestUsers(t, ss, func(i int) *models.CreateUserCommand { - return &models.CreateUserCommand{ - Email: fmt.Sprint("user", i, "@test.com"), - Name: fmt.Sprint("user", i), - Login: fmt.Sprint("loginuser", i), - IsDisabled: false, - } - }) - - userIdsToDisable := []int64{} - for i := 0; i < 3; i++ { - userIdsToDisable = append(userIdsToDisable, users[i].Id) - } - disableCmd := models.BatchDisableUsersCommand{ - UserIds: userIdsToDisable, - IsDisabled: true, - } - - err := BatchDisableUsers(&disableCmd) - So(err, ShouldBeNil) - - query := models.SearchUsersQuery{} - err = SearchUsers(&query) - - So(err, ShouldBeNil) - So(query.Result.TotalCount, ShouldEqual, 5) - for _, user := range query.Result.Users { - shouldBeDisabled := false - - // Check if user id is in the userIdsToDisable list - for _, disabledUserId := range userIdsToDisable { - if user.Id == disabledUserId { - So(user.IsDisabled, ShouldBeTrue) - shouldBeDisabled = true - } - } - - // Otherwise user shouldn't be disabled - if !shouldBeDisabled { - So(user.IsDisabled, ShouldBeFalse) - } - } - }) - - // Since previous tests were destructive - ss = InitTestDB(t) - users = createFiveTestUsers(t, ss, func(i int) *models.CreateUserCommand { - return &models.CreateUserCommand{ - Email: fmt.Sprint("user", i, "@test.com"), - Name: fmt.Sprint("user", i), - Login: fmt.Sprint("loginuser", i), - IsDisabled: false, - } - }) - }) - - Convey("When searching users", func() { - // Find a user to set tokens on - login := "loginuser0" - - // Calling GetUserByAuthInfoQuery on an existing user will populate an entry in the user_auth table - // Make the first log-in during the past - getTime = func() time.Time { return time.Now().AddDate(0, 0, -2) } - query := &models.GetUserByAuthInfoQuery{Login: login, AuthModule: "ldap", AuthId: "ldap0"} - err := GetUserByAuthInfo(query) - getTime = time.Now - - So(err, ShouldBeNil) - So(query.Result.Login, ShouldEqual, login) - - // Add a second auth module for this user - // Have this module's last log-in be more recent - getTime = func() time.Time { return time.Now().AddDate(0, 0, -1) } - query = &models.GetUserByAuthInfoQuery{Login: login, AuthModule: "oauth", AuthId: "oauth0"} - err = GetUserByAuthInfo(query) - getTime = time.Now - - So(err, ShouldBeNil) - So(query.Result.Login, ShouldEqual, login) - - Convey("Should return the only most recently used auth_module", func() { - searchUserQuery := &models.SearchUsersQuery{} - err = SearchUsers(searchUserQuery) - - So(err, ShouldBeNil) - So(searchUserQuery.Result.Users, ShouldHaveLength, 5) - for _, user := range searchUserQuery.Result.Users { - if user.Login == login { - So(user.AuthModule, ShouldHaveLength, 1) - So(user.AuthModule[0], ShouldEqual, "oauth") - } - } - - // "log in" again with the first auth module - updateAuthCmd := &models.UpdateAuthInfoCommand{UserId: query.Result.Id, AuthModule: "ldap", AuthId: "ldap1"} - err = UpdateAuthInfo(updateAuthCmd) - So(err, ShouldBeNil) - - searchUserQuery = &models.SearchUsersQuery{} - err = SearchUsers(searchUserQuery) - - So(err, ShouldBeNil) - for _, user := range searchUserQuery.Result.Users { - if user.Login == login { - So(user.AuthModule, ShouldHaveLength, 1) - So(user.AuthModule[0], ShouldEqual, "ldap") - } - } - }) - }) - - Convey("When searching LDAP users", func() { - for i := 0; i < 5; i++ { - // Find a user to set tokens on - login := fmt.Sprint("loginuser", i) - - // Calling GetUserByAuthInfoQuery on an existing user will populate an entry in the user_auth table - // Make the first log-in during the past - getTime = func() time.Time { return time.Now().AddDate(0, 0, -2) } - query := &models.GetUserByAuthInfoQuery{Login: login, AuthModule: "ldap", AuthId: fmt.Sprint("ldap", i)} - err := GetUserByAuthInfo(query) - getTime = time.Now - - So(err, ShouldBeNil) - So(query.Result.Login, ShouldEqual, login) - } - - // Log in first user with oauth - login := "loginuser0" - getTime = func() time.Time { return time.Now().AddDate(0, 0, -1) } - query := &models.GetUserByAuthInfoQuery{Login: login, AuthModule: "oauth", AuthId: "oauth0"} - err := GetUserByAuthInfo(query) - getTime = time.Now - - So(err, ShouldBeNil) - So(query.Result.Login, ShouldEqual, login) - - Convey("Should only return users recently logged in with ldap when filtered by ldap auth module", func() { - searchUserQuery := &models.SearchUsersQuery{AuthModule: "ldap"} - err = SearchUsers(searchUserQuery) - - So(err, ShouldBeNil) - So(searchUserQuery.Result.Users, ShouldHaveLength, 4) - for _, user := range searchUserQuery.Result.Users { - if user.Login == login { - So(user.AuthModule, ShouldHaveLength, 1) - So(user.AuthModule[0], ShouldEqual, "ldap") - } - } - }) - }) - }) - - Convey("Given one grafana admin user", func() { - createUserCmd := models.CreateUserCommand{ - Email: fmt.Sprint("admin", "@test.com"), - Name: "admin", - Login: "admin", - IsAdmin: true, + disableCmd := models.BatchDisableUsersCommand{ + UserIds: []int64{1, 2, 3, 4, 5}, + IsDisabled: false, + } + + err := BatchDisableUsers(&disableCmd) + require.Nil(t, err) + + isDisabled := false + query := &models.SearchUsersQuery{IsDisabled: &isDisabled} + err = SearchUsers(query) + + require.Nil(t, err) + require.EqualValues(t, query.Result.TotalCount, 5) + }) + + ss = InitTestDB(t) + + t.Run("Testing DB - disable only specific users", func(t *testing.T) { + users := createFiveTestUsers(t, ss, func(i int) *models.CreateUserCommand { + return &models.CreateUserCommand{ + Email: fmt.Sprint("user", i, "@test.com"), + Name: fmt.Sprint("user", i), + Login: fmt.Sprint("loginuser", i), + IsDisabled: false, } - user, err := ss.CreateUser(context.Background(), createUserCmd) - So(err, ShouldBeNil) - - Convey("Cannot make themselves a non-admin", func() { - updatePermsError := ss.UpdateUserPermissions(1, false) - - So(updatePermsError, ShouldEqual, models.ErrLastGrafanaAdmin) - - query := models.GetUserByIdQuery{Id: user.Id} - getUserError := GetUserById(&query) - So(getUserError, ShouldBeNil) - - So(query.Result.IsAdmin, ShouldEqual, true) - }) }) - Convey("Given one user", func() { - const email = "user@test.com" - const username = "user" - createUserCmd := models.CreateUserCommand{ - Email: email, - Name: "user", - Login: username, + userIdsToDisable := []int64{} + for i := 0; i < 3; i++ { + userIdsToDisable = append(userIdsToDisable, users[i].Id) + } + disableCmd := models.BatchDisableUsersCommand{ + UserIds: userIdsToDisable, + IsDisabled: true, + } + + err := BatchDisableUsers(&disableCmd) + require.Nil(t, err) + + query := models.SearchUsersQuery{} + err = SearchUsers(&query) + + require.Nil(t, err) + require.EqualValues(t, query.Result.TotalCount, 5) + for _, user := range query.Result.Users { + shouldBeDisabled := false + + // Check if user id is in the userIdsToDisable list + for _, disabledUserId := range userIdsToDisable { + if user.Id == disabledUserId { + require.True(t, user.IsDisabled) + shouldBeDisabled = true + } } - _, err := ss.CreateUser(context.Background(), createUserCmd) - So(err, ShouldBeNil) - Convey("When trying to create a new user with the same email, an error is returned", func() { - createUserCmd := models.CreateUserCommand{ - Email: email, - Name: "user2", - Login: "user2", - SkipOrgSetup: true, - } - _, err := ss.CreateUser(context.Background(), createUserCmd) - So(err, ShouldEqual, models.ErrUserAlreadyExists) - }) + // Otherwise user shouldn't be disabled + if !shouldBeDisabled { + require.False(t, user.IsDisabled) + } + } + }) - Convey("When trying to create a new user with the same login, an error is returned", func() { - createUserCmd := models.CreateUserCommand{ - Email: "user2@test.com", - Name: "user2", - Login: username, - SkipOrgSetup: true, - } - _, err := ss.CreateUser(context.Background(), createUserCmd) - So(err, ShouldEqual, models.ErrUserAlreadyExists) - }) + ss = InitTestDB(t) + + t.Run("Testing DB - search users", func(t *testing.T) { + // Since previous tests were destructive + createFiveTestUsers(t, ss, func(i int) *models.CreateUserCommand { + return &models.CreateUserCommand{ + Email: fmt.Sprint("user", i, "@test.com"), + Name: fmt.Sprint("user", i), + Login: fmt.Sprint("loginuser", i), + IsDisabled: false, + } }) + // Find a user to set tokens on + login := "loginuser0" + + // Calling GetUserByAuthInfoQuery on an existing user will populate an entry in the user_auth table + // Make the first log-in during the past + getTime = func() time.Time { return time.Now().AddDate(0, 0, -2) } + query := &models.GetUserByAuthInfoQuery{Login: login, AuthModule: "ldap", AuthId: "ldap0"} + err := GetUserByAuthInfo(query) + getTime = time.Now + + require.Nil(t, err) + require.Equal(t, query.Result.Login, login) + + // Add a second auth module for this user + // Have this module's last log-in be more recent + getTime = func() time.Time { return time.Now().AddDate(0, 0, -1) } + query = &models.GetUserByAuthInfoQuery{Login: login, AuthModule: "oauth", AuthId: "oauth0"} + err = GetUserByAuthInfo(query) + getTime = time.Now + + require.Nil(t, err) + require.Equal(t, query.Result.Login, login) + + // Return the only most recently used auth_module + searchUserQuery := &models.SearchUsersQuery{} + err = SearchUsers(searchUserQuery) + + require.Nil(t, err) + require.Len(t, searchUserQuery.Result.Users, 5) + for _, user := range searchUserQuery.Result.Users { + if user.Login == login { + require.Len(t, user.AuthModule, 1) + require.Equal(t, user.AuthModule[0], "oauth") + } + } + + // "log in" again with the first auth module + updateAuthCmd := &models.UpdateAuthInfoCommand{UserId: query.Result.Id, AuthModule: "ldap", AuthId: "ldap1"} + err = UpdateAuthInfo(updateAuthCmd) + require.Nil(t, err) + + searchUserQuery = &models.SearchUsersQuery{} + err = SearchUsers(searchUserQuery) + + require.Nil(t, err) + for _, user := range searchUserQuery.Result.Users { + if user.Login == login { + require.Len(t, user.AuthModule, 1) + require.Equal(t, user.AuthModule[0], "ldap") + } + } + + // Re-init DB + ss = InitTestDB(t) + createFiveTestUsers(t, ss, func(i int) *models.CreateUserCommand { + return &models.CreateUserCommand{ + Email: fmt.Sprint("user", i, "@test.com"), + Name: fmt.Sprint("user", i), + Login: fmt.Sprint("loginuser", i), + IsDisabled: false, + } + }) + + // Search LDAP users + for i := 0; i < 5; i++ { + // Find a user to set tokens on + login = fmt.Sprint("loginuser", i) + + // Calling GetUserByAuthInfoQuery on an existing user will populate an entry in the user_auth table + // Make the first log-in during the past + getTime = func() time.Time { return time.Now().AddDate(0, 0, -2) } + query = &models.GetUserByAuthInfoQuery{Login: login, AuthModule: "ldap", AuthId: fmt.Sprint("ldap", i)} + err = GetUserByAuthInfo(query) + getTime = time.Now + + require.Nil(t, err) + require.Equal(t, query.Result.Login, login) + } + + // Log in first user with oauth + login = "loginuser0" + getTime = func() time.Time { return time.Now().AddDate(0, 0, -1) } + query = &models.GetUserByAuthInfoQuery{Login: login, AuthModule: "oauth", AuthId: "oauth0"} + err = GetUserByAuthInfo(query) + getTime = time.Now + + require.Nil(t, err) + require.Equal(t, query.Result.Login, login) + + // Should only return users recently logged in with ldap when filtered by ldap auth module + searchUserQuery = &models.SearchUsersQuery{AuthModule: "ldap"} + err = SearchUsers(searchUserQuery) + + require.Nil(t, err) + require.Len(t, searchUserQuery.Result.Users, 4) + for _, user := range searchUserQuery.Result.Users { + if user.Login == login { + require.Len(t, user.AuthModule, 1) + require.Equal(t, user.AuthModule[0], "ldap") + } + } + }) + + t.Run("Testing DB - grafana admin users", func(t *testing.T) { + + ss = InitTestDB(t) + + createUserCmd := models.CreateUserCommand{ + Email: fmt.Sprint("admin", "@test.com"), + Name: "admin", + Login: "admin", + IsAdmin: true, + } + user, err := ss.CreateUser(context.Background(), createUserCmd) + require.Nil(t, err) + + // Cannot make themselves a non-admin + updatePermsError := ss.UpdateUserPermissions(1, false) + + require.Equal(t, updatePermsError, models.ErrLastGrafanaAdmin) + + query := models.GetUserByIdQuery{Id: user.Id} + getUserError := GetUserById(&query) + require.Nil(t, getUserError) + + require.True(t, query.Result.IsAdmin) + + // One user + const email = "user@test.com" + const username = "user" + createUserCmd = models.CreateUserCommand{ + Email: email, + Name: "user", + Login: username, + } + _, err = ss.CreateUser(context.Background(), createUserCmd) + require.Nil(t, err) + + // When trying to create a new user with the same email, an error is returned + createUserCmd = models.CreateUserCommand{ + Email: email, + Name: "user2", + Login: "user2", + SkipOrgSetup: true, + } + _, err = ss.CreateUser(context.Background(), createUserCmd) + require.Equal(t, err, models.ErrUserAlreadyExists) + + // When trying to create a new user with the same login, an error is returned + createUserCmd = models.CreateUserCommand{ + Email: "user2@test.com", + Name: "user2", + Login: username, + SkipOrgSetup: true, + } + _, err = ss.CreateUser(context.Background(), createUserCmd) + require.Equal(t, err, models.ErrUserAlreadyExists) }) } @@ -625,7 +671,7 @@ func createFiveTestUsers(t *testing.T, sqlStore *SQLStore, fn func(i int) *model user, err := sqlStore.CreateUser(context.Background(), *cmd) users = append(users, *user) - So(err, ShouldBeNil) + require.Nil(t, err) } return users