package userimpl import ( "context" "fmt" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/grafana/grafana/pkg/infra/db" "github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/org/orgimpl" "github.com/grafana/grafana/pkg/services/quota/quotaimpl" "github.com/grafana/grafana/pkg/services/searchusers/sortopts" "github.com/grafana/grafana/pkg/services/sqlstore/migrator" "github.com/grafana/grafana/pkg/services/supportbundles/supportbundlestest" "github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/tests/testsuite" ) func TestMain(m *testing.M) { testsuite.Run(m) } func TestIntegrationUserGet(t *testing.T) { testCases := []struct { name string wantErr error searchLogin string searchEmail string caseInsensitive bool }{ { name: "user not found non exact - not case insensitive", wantErr: user.ErrUserNotFound, searchLogin: "Test", searchEmail: "Test@email.com", caseInsensitive: false, }, { name: "user found exact - not case insensitive", wantErr: nil, searchLogin: "test", searchEmail: "test@email.com", caseInsensitive: false, }, { name: "user found non exact - case insensitive", wantErr: nil, searchLogin: "Test", searchEmail: "Test@email.com", caseInsensitive: true, }, { name: "user found exact - case insensitive", wantErr: nil, searchLogin: "Test", searchEmail: "Test@email.com", caseInsensitive: true, }, { name: "user not found - case insensitive", wantErr: user.ErrUserNotFound, searchLogin: "Test_login", searchEmail: "Test*@email.com", caseInsensitive: true, }, } if testing.Short() { t.Skip("skipping integration test") } ss := db.InitTestDB(t) cfg := ss.Cfg userStore := ProvideStore(ss, cfg) _, errUser := userStore.Insert(context.Background(), &user.User{ Email: "test@email.com", Name: "test", Login: "test", Created: time.Now(), Updated: time.Now(), }, ) require.NoError(t, errUser) for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { if !tc.caseInsensitive && db.IsTestDbMySQL() { t.Skip("mysql is always case insensitive") } cfg.CaseInsensitiveLogin = tc.caseInsensitive usr, err := userStore.Get(context.Background(), &user.User{ Email: tc.searchEmail, Login: tc.searchLogin, }, ) if tc.wantErr != nil { require.Error(t, err) require.Nil(t, usr) } else { require.NoError(t, err) require.NotNil(t, usr) require.NotEmpty(t, usr.UID) } }) } } func TestIntegrationUserDataAccess(t *testing.T) { if testing.Short() { t.Skip("skipping integration test") } ss := db.InitTestDB(t) quotaService := quotaimpl.ProvideService(ss, ss.Cfg) orgService, err := orgimpl.ProvideService(ss, ss.Cfg, quotaService) require.NoError(t, err) userStore := ProvideStore(ss, setting.NewCfg()) usrSvc, err := ProvideService(ss, orgService, ss.Cfg, nil, nil, quotaService, supportbundlestest.NewFakeBundleService()) require.NoError(t, err) usr := &user.SignedInUser{ OrgID: 1, Permissions: map[int64]map[string][]string{1: {"users:read": {"global.users:*"}}}, } t.Run("user not found", func(t *testing.T) { _, err := userStore.Get(context.Background(), &user.User{ Email: "test@email.com", Name: "test1", Login: "test1", }, ) require.Error(t, err, user.ErrUserNotFound) }) t.Run("insert user", func(t *testing.T) { _, err := userStore.Insert(context.Background(), &user.User{ Email: "test@email.com", Name: "test1", Login: "test1", Created: time.Now(), Updated: time.Now(), }, ) require.NoError(t, err) }) t.Run("insert user (with known UID)", func(t *testing.T) { ctx := context.Background() id, err := userStore.Insert(ctx, &user.User{ UID: "abcd", Email: "next-test@email.com", Name: "next-test1", Login: "next-test1", Created: time.Now(), Updated: time.Now(), }, ) require.NoError(t, err) found, err := userStore.GetByID(ctx, id) require.NoError(t, err) require.Equal(t, "abcd", found.UID) siu, err := userStore.GetSignedInUser(ctx, &user.GetSignedInUserQuery{ UserID: id, OrgID: found.OrgID, }) require.NoError(t, err) require.Equal(t, "abcd", siu.UserUID) }) t.Run("get user", func(t *testing.T) { _, err := userStore.Get(context.Background(), &user.User{ Email: "test@email.com", Name: "test1", Login: "test1", }, ) require.NoError(t, err) }) t.Run("Testing DB - creates and loads user", func(t *testing.T) { ss := db.InitTestDB(t) _, usrSvc := createOrgAndUserSvc(t, ss, ss.Cfg) cmd := user.CreateUserCommand{ Email: "usertest@test.com", Name: "user name", Login: "user_test_login", } usr, err := usrSvc.Create(context.Background(), &cmd) require.NoError(t, err) result, err := userStore.GetByID(context.Background(), usr.ID) require.Nil(t, err) require.Equal(t, result.Email, "usertest@test.com") require.Equal(t, result.Password, "") require.Len(t, result.Rands, 10) require.Len(t, result.Salt, 10) require.False(t, result.IsDisabled) result, err = userStore.GetByID(context.Background(), usr.ID) require.Nil(t, err) require.Equal(t, result.Email, "usertest@test.com") require.Equal(t, result.Password, "") require.Len(t, result.Rands, 10) require.Len(t, result.Salt, 10) require.False(t, result.IsDisabled) t.Run("Get User by email case insensitive", func(t *testing.T) { userStore.cfg.CaseInsensitiveLogin = true query := user.GetUserByEmailQuery{Email: "USERtest@TEST.COM"} result, err := userStore.GetByEmail(context.Background(), &query) require.Nil(t, err) require.Equal(t, result.Email, "usertest@test.com") require.Equal(t, result.Password, "") require.Len(t, result.Rands, 10) require.Len(t, result.Salt, 10) require.False(t, result.IsDisabled) userStore.cfg.CaseInsensitiveLogin = false }) t.Run("Testing DB - creates and loads user", func(t *testing.T) { result, err = userStore.GetByID(context.Background(), usr.ID) require.Nil(t, err) require.Equal(t, result.Email, "usertest@test.com") require.Equal(t, result.Password, "") require.Len(t, result.Rands, 10) require.Len(t, result.Salt, 10) require.False(t, result.IsDisabled) result, err = userStore.GetByID(context.Background(), usr.ID) require.Nil(t, err) require.Equal(t, result.Email, "usertest@test.com") require.Equal(t, result.Password, "") require.Len(t, result.Rands, 10) require.Len(t, result.Salt, 10) require.False(t, result.IsDisabled) ss.Cfg.CaseInsensitiveLogin = false }) }) t.Run("Testing DB - error on case insensitive conflict", func(t *testing.T) { if ss.GetDBType() == migrator.MySQL { t.Skip("Skipping on MySQL due to case insensitive indexes") } userStore.cfg.CaseInsensitiveLogin = true cmd := user.CreateUserCommand{ Email: "confusertest@test.com", Name: "user name", Login: "user_email_conflict", } // userEmailConflict _, err = usrSvc.Create(context.Background(), &cmd) require.NoError(t, err) cmd = user.CreateUserCommand{ Email: "confusertest@TEST.COM", Name: "user name", Login: "user_email_conflict_two", } _, err := usrSvc.Create(context.Background(), &cmd) require.NoError(t, err) cmd = user.CreateUserCommand{ Email: "user_test_login_conflict@test.com", Name: "user name", Login: "user_test_login_conflict", } // userLoginConflict _, err = usrSvc.Create(context.Background(), &cmd) require.NoError(t, err) cmd = user.CreateUserCommand{ Email: "user_test_login_conflict_two@test.com", Name: "user name", Login: "user_test_login_CONFLICT", } _, err = usrSvc.Create(context.Background(), &cmd) require.NoError(t, err) ss.Cfg.CaseInsensitiveLogin = true t.Run("GetByEmail - email conflict", func(t *testing.T) { query := user.GetUserByEmailQuery{Email: "confusertest@test.com"} _, err = userStore.GetByEmail(context.Background(), &query) require.Error(t, err) }) t.Run("GetByEmail - login conflict", func(t *testing.T) { query := user.GetUserByEmailQuery{Email: "user_test_login_conflict@test.com"} _, err = userStore.GetByEmail(context.Background(), &query) require.Error(t, err) }) t.Run("GetByLogin - email conflict", func(t *testing.T) { query := user.GetUserByLoginQuery{LoginOrEmail: "user_email_conflict_two"} _, err = userStore.GetByLogin(context.Background(), &query) require.Error(t, err) }) t.Run("GetByLogin - login conflict", func(t *testing.T) { query := user.GetUserByLoginQuery{LoginOrEmail: "user_test_login_conflict"} _, err = userStore.GetByLogin(context.Background(), &query) require.Error(t, err) }) t.Run("GetByLogin - login conflict by email", func(t *testing.T) { query := user.GetUserByLoginQuery{LoginOrEmail: "user_test_login_conflict@test.com"} _, err = userStore.GetByLogin(context.Background(), &query) require.Error(t, err) }) t.Run("GetByLogin - user2 uses user1.email as login", func(t *testing.T) { // create user_1 user1 := &user.User{ Email: "user_1@mail.com", Name: "user_1", Login: "user_1", Password: "user_1_password", Created: time.Now(), Updated: time.Now(), IsDisabled: true, } _, err := userStore.Insert(context.Background(), user1) require.Nil(t, err) // create user_2 user2 := &user.User{ Email: "user_2@mail.com", Name: "user_2", Login: "user_1@mail.com", Password: "user_2_password", Created: time.Now(), Updated: time.Now(), IsDisabled: true, } _, err = userStore.Insert(context.Background(), user2) require.Nil(t, err) // query user database for user_1 email query := user.GetUserByLoginQuery{LoginOrEmail: "user_1@mail.com"} result, err := userStore.GetByLogin(context.Background(), &query) require.Nil(t, err) // expect user_1 as result require.Equal(t, user1.Email, result.Email) require.Equal(t, user1.Login, result.Login) require.Equal(t, user1.Name, result.Name) require.NotEqual(t, user2.Email, result.Email) require.NotEqual(t, user2.Login, result.Login) require.NotEqual(t, user2.Name, result.Name) }) ss.Cfg.CaseInsensitiveLogin = false }) t.Run("Change user password", func(t *testing.T) { err := userStore.ChangePassword(context.Background(), &user.ChangeUserPasswordCommand{}) require.NoError(t, err) }) t.Run("update last seen at", func(t *testing.T) { err := userStore.UpdateLastSeenAt(context.Background(), &user.UpdateUserLastSeenAtCommand{ UserID: 10, // Requires UserID }) require.NoError(t, err) err = userStore.UpdateLastSeenAt(context.Background(), &user.UpdateUserLastSeenAtCommand{ UserID: -1, }) require.Error(t, err) }) t.Run("get signed in user", func(t *testing.T) { ss := db.InitTestDB(t) orgService, usrSvc := createOrgAndUserSvc(t, ss, ss.Cfg) users := createFiveTestUsers(t, usrSvc, func(i int) *user.CreateUserCommand { return &user.CreateUserCommand{ Email: fmt.Sprint("user", i, "@test.com"), Name: fmt.Sprint("user", i), Login: fmt.Sprint("loginuser", i), IsDisabled: false, } }) err := orgService.AddOrgUser(context.Background(), &org.AddOrgUserCommand{ LoginOrEmail: users[1].Login, Role: org.RoleViewer, OrgID: users[0].OrgID, UserID: users[1].ID, }) require.Nil(t, err) query := &user.GetSignedInUserQuery{OrgID: users[1].OrgID, UserID: users[1].ID} result, err := userStore.GetSignedInUser(context.Background(), query) require.NoError(t, err) require.Equal(t, result.Email, "user1@test.com") // Throw errors for invalid user IDs for _, userID := range []int64{-1, 0} { _, err = userStore.GetSignedInUser(context.Background(), &user.GetSignedInUserQuery{ OrgID: users[1].OrgID, UserID: userID}) // zero require.Error(t, err) } }) t.Run("update user", func(t *testing.T) { err := userStore.UpdateUser(context.Background(), &user.User{ID: 1, Name: "testtestest", Login: "loginloginlogin"}) require.NoError(t, err) result, err := userStore.GetByID(context.Background(), 1) require.NoError(t, err) assert.Equal(t, result.Name, "testtestest") assert.Equal(t, result.Login, "loginloginlogin") }) t.Run("Testing DB - grafana admin users", func(t *testing.T) { ss := db.InitTestDB(t) _, usrSvc := createOrgAndUserSvc(t, ss, ss.Cfg) createUserCmd := user.CreateUserCommand{ Email: fmt.Sprint("admin", "@test.com"), Name: "admin", Login: "admin", IsAdmin: true, } usr, err := usrSvc.Create(context.Background(), &createUserCmd) require.Nil(t, err) // Cannot make themselves a non-admin updatePermsError := userStore.UpdatePermissions(context.Background(), usr.ID, false) require.Equal(t, user.ErrLastGrafanaAdmin, updatePermsError) query := user.GetUserByIDQuery{ID: usr.ID} queryResult, getUserError := userStore.GetByID(context.Background(), query.ID) require.Nil(t, getUserError) require.True(t, queryResult.IsAdmin) // One user const email = "user@test.com" const username = "user" createUserCmd = user.CreateUserCommand{ Email: email, Name: "user", Login: username, } _, err = usrSvc.Create(context.Background(), &createUserCmd) require.Nil(t, err) // When trying to create a new user with the same email, an error is returned createUserCmd = user.CreateUserCommand{ Email: email, Name: "user2", Login: "user2", SkipOrgSetup: true, } _, err = usrSvc.Create(context.Background(), &createUserCmd) require.Equal(t, user.ErrUserAlreadyExists, err) // When trying to create a new user with the same login, an error is returned createUserCmd = user.CreateUserCommand{ Email: "user2@test.com", Name: "user2", Login: username, SkipOrgSetup: true, } _, err = usrSvc.Create(context.Background(), &createUserCmd) require.Equal(t, user.ErrUserAlreadyExists, err) }) t.Run("GetProfile", func(t *testing.T) { _, err := userStore.GetProfile(context.Background(), &user.GetUserProfileQuery{UserID: 1}) require.NoError(t, err) }) t.Run("SetHelpFlag", func(t *testing.T) { err := userStore.SetHelpFlag(context.Background(), &user.SetUserHelpFlagCommand{UserID: 1, HelpFlags1: user.HelpFlags1(1)}) require.NoError(t, err) }) t.Run("Testing DB - return list users based on their is_disabled flag", func(t *testing.T) { ss = db.InitTestDB(t) _, usrSvc := createOrgAndUserSvc(t, ss, ss.Cfg) userStore := ProvideStore(ss, ss.Cfg) createFiveTestUsers(t, usrSvc, func(i int) *user.CreateUserCommand { return &user.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 := user.SearchUsersQuery{IsDisabled: &isDisabled, SignedInUser: usr} result, err := userStore.Search(context.Background(), &query) require.Nil(t, err) require.Len(t, result.Users, 2) first, third := false, false for _, user := range 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 := db.InitTestDB(t) orgService, usrSvc = createOrgAndUserSvc(t, ss, ss.Cfg) users := createFiveTestUsers(t, usrSvc, func(i int) *user.CreateUserCommand { return &user.CreateUserCommand{ Email: fmt.Sprint("user", i, "@test.com"), Name: fmt.Sprint("user", i), Login: fmt.Sprint("loginuser", i), IsDisabled: false, } }) err = orgService.AddOrgUser(context.Background(), &org.AddOrgUserCommand{ LoginOrEmail: users[1].Login, Role: org.RoleViewer, OrgID: users[0].OrgID, UserID: users[1].ID, }) require.Nil(t, err) // When the user is deleted err = userStore.Delete(context.Background(), users[1].ID) require.Nil(t, err) // A user is an org member and has been assigned permissions // Re-init DB ss = db.InitTestDB(t) orgService, usrSvc = createOrgAndUserSvc(t, ss, ss.Cfg) users = createFiveTestUsers(t, usrSvc, func(i int) *user.CreateUserCommand { return &user.CreateUserCommand{ Email: fmt.Sprint("user", i, "@test.com"), Name: fmt.Sprint("user", i), Login: fmt.Sprint("loginuser", i), IsDisabled: false, } }) err = orgService.AddOrgUser(context.Background(), &org.AddOrgUserCommand{ LoginOrEmail: users[1].Login, Role: org.RoleViewer, OrgID: users[0].OrgID, UserID: users[1].ID, }) require.Nil(t, err) query3 := &user.GetSignedInUserQuery{OrgID: users[1].OrgID, UserID: users[1].ID} query3Result, err := userStore.GetSignedInUser(context.Background(), query3) require.Nil(t, err) require.NotNil(t, query3Result) require.Equal(t, query3.OrgID, users[1].OrgID) disableCmd := user.BatchDisableUsersCommand{ UserIDs: []int64{users[0].ID, users[1].ID, users[2].ID, users[3].ID, users[4].ID}, IsDisabled: true, } err = userStore.BatchDisableUsers(context.Background(), &disableCmd) require.Nil(t, err) isDisabled = true query5 := &user.SearchUsersQuery{IsDisabled: &isDisabled, SignedInUser: usr} query5Result, err := userStore.Search(context.Background(), query5) require.Nil(t, err) require.EqualValues(t, query5Result.TotalCount, 5) // the user is deleted err = userStore.Delete(context.Background(), users[1].ID) require.Nil(t, err) }) t.Run("Testing DB - return list of users that the SignedInUser has permission to read", func(t *testing.T) { ss := db.InitTestDB(t) orgService, err := orgimpl.ProvideService(ss, ss.Cfg, quotaService) require.NoError(t, err) usrSvc, err := ProvideService(ss, orgService, ss.Cfg, nil, nil, quotaService, supportbundlestest.NewFakeBundleService()) require.NoError(t, err) createFiveTestUsers(t, usrSvc, func(i int) *user.CreateUserCommand { return &user.CreateUserCommand{ Email: fmt.Sprint("user", i, "@test.com"), Name: fmt.Sprint("user", i), Login: fmt.Sprint("loginuser", i), } }) testUser := &user.SignedInUser{ OrgID: 1, Permissions: map[int64]map[string][]string{1: {"users:read": {"global.users:id:1", "global.users:id:3"}}}, } query := user.SearchUsersQuery{SignedInUser: testUser} queryResult, err := userStore.Search(context.Background(), &query) assert.Nil(t, err) assert.Len(t, queryResult.Users, 2) }) ss = db.InitTestDB(t) t.Run("Testing DB - enable all users", func(t *testing.T) { users := createFiveTestUsers(t, usrSvc, func(i int) *user.CreateUserCommand { return &user.CreateUserCommand{ Email: fmt.Sprint("user", i, "@test.com"), Name: fmt.Sprint("user", i), Login: fmt.Sprint("loginuser", i), IsDisabled: true, } }) disableCmd := user.BatchDisableUsersCommand{ UserIDs: []int64{users[0].ID, users[1].ID, users[2].ID, users[3].ID, users[4].ID}, IsDisabled: false, } err := userStore.BatchDisableUsers(context.Background(), &disableCmd) require.Nil(t, err) isDisabled := false query := &user.SearchUsersQuery{IsDisabled: &isDisabled, SignedInUser: usr} queryResult, err := userStore.Search(context.Background(), query) require.Nil(t, err) require.EqualValues(t, queryResult.TotalCount, 5) }) t.Run("Can search users", func(t *testing.T) { ss = db.InitTestDB(t) userStore.cfg.AutoAssignOrg = false ac1cmd := user.CreateUserCommand{Login: "ac1", Email: "ac1@test.com", Name: "ac1 name"} ac2cmd := user.CreateUserCommand{Login: "ac2", Email: "ac2@test.com", Name: "ac2 name", IsAdmin: true} serviceaccountcmd := user.CreateUserCommand{Login: "serviceaccount", Email: "service@test.com", Name: "serviceaccount name", IsAdmin: true, IsServiceAccount: true} _, err := usrSvc.Create(context.Background(), &ac1cmd) require.NoError(t, err) _, err = usrSvc.Create(context.Background(), &ac2cmd) require.NoError(t, err) // user only used for making sure we filter out the service accounts _, err = usrSvc.Create(context.Background(), &serviceaccountcmd) require.NoError(t, err) query := user.SearchUsersQuery{Query: "", SignedInUser: &user.SignedInUser{ OrgID: 1, Permissions: map[int64]map[string][]string{ 1: {accesscontrol.ActionUsersRead: {accesscontrol.ScopeGlobalUsersAll}}, }, }} queryResult, err := userStore.Search(context.Background(), &query) require.NoError(t, err) require.Len(t, queryResult.Users, 2) require.Equal(t, queryResult.Users[0].Email, "ac1@test.com") require.Equal(t, queryResult.Users[1].Email, "ac2@test.com") }) ss = db.InitTestDB(t) t.Run("Testing DB - disable only specific users", func(t *testing.T) { users := createFiveTestUsers(t, usrSvc, func(i int) *user.CreateUserCommand { return &user.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 := user.BatchDisableUsersCommand{ UserIDs: userIdsToDisable, IsDisabled: true, } err := userStore.BatchDisableUsers(context.Background(), &disableCmd) require.Nil(t, err) query := user.SearchUsersQuery{SignedInUser: usr} queryResult, err := userStore.Search(context.Background(), &query) require.Nil(t, err) require.EqualValues(t, queryResult.TotalCount, 5) for _, user := range queryResult.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 } } // Otherwise user shouldn't be disabled if !shouldBeDisabled { require.False(t, user.IsDisabled) } } }) ss = db.InitTestDB(t) t.Run("Testing DB - search users", func(t *testing.T) { // Since previous tests were destructive createFiveTestUsers(t, usrSvc, func(i int) *user.CreateUserCommand { return &user.CreateUserCommand{ Email: fmt.Sprint("user", i, "@test.com"), Name: fmt.Sprint("user", i), Login: fmt.Sprint("loginuser", i), IsDisabled: false, } }) }) t.Run("Disable user", func(t *testing.T) { id, err := userStore.Insert(context.Background(), &user.User{ Name: "user111", Created: time.Now(), Updated: time.Now(), }) require.NoError(t, err) err = userStore.Disable(context.Background(), &user.DisableUserCommand{UserID: id}) require.NoError(t, err) }) t.Run("Testing DB - multiple users", func(t *testing.T) { ss = db.InitTestDB(t) createFiveTestUsers(t, usrSvc, func(i int) *user.CreateUserCommand { return &user.CreateUserCommand{ Email: fmt.Sprint("user", i, "@test.com"), Name: fmt.Sprint("user", i), Login: fmt.Sprint("loginuser", i), IsDisabled: false, } }) // Return the first page of users and a total count query := user.SearchUsersQuery{Query: "", Page: 1, Limit: 3, SignedInUser: usr} queryResult, err := userStore.Search(context.Background(), &query) require.Nil(t, err) require.Len(t, queryResult.Users, 3) require.EqualValues(t, queryResult.TotalCount, 5) // Return the second page of users and a total count query = user.SearchUsersQuery{Query: "", Page: 2, Limit: 3, SignedInUser: usr} queryResult, err = userStore.Search(context.Background(), &query) require.Nil(t, err) require.Len(t, queryResult.Users, 2) require.EqualValues(t, queryResult.TotalCount, 5) // Return list of users matching query on user name query = user.SearchUsersQuery{Query: "use", Page: 1, Limit: 3, SignedInUser: usr} queryResult, err = userStore.Search(context.Background(), &query) require.Nil(t, err) require.Len(t, queryResult.Users, 3) require.EqualValues(t, queryResult.TotalCount, 5) query = user.SearchUsersQuery{Query: "ser1", Page: 1, Limit: 3, SignedInUser: usr} queryResult, err = userStore.Search(context.Background(), &query) require.Nil(t, err) require.Len(t, queryResult.Users, 1) require.EqualValues(t, queryResult.TotalCount, 1) query = user.SearchUsersQuery{Query: "USER1", Page: 1, Limit: 3, SignedInUser: usr} queryResult, err = userStore.Search(context.Background(), &query) require.Nil(t, err) require.Len(t, queryResult.Users, 1) require.EqualValues(t, queryResult.TotalCount, 1) query = user.SearchUsersQuery{Query: "idontexist", Page: 1, Limit: 3, SignedInUser: usr} queryResult, err = userStore.Search(context.Background(), &query) require.Nil(t, err) require.Len(t, queryResult.Users, 0) require.EqualValues(t, queryResult.TotalCount, 0) // Return list of users matching query on email query = user.SearchUsersQuery{Query: "ser1@test.com", Page: 1, Limit: 3, SignedInUser: usr} queryResult, err = userStore.Search(context.Background(), &query) require.Nil(t, err) require.Len(t, queryResult.Users, 1) require.EqualValues(t, queryResult.TotalCount, 1) // Return list of users matching query on login name query = user.SearchUsersQuery{Query: "loginuser1", Page: 1, Limit: 3, SignedInUser: usr} queryResult, err = userStore.Search(context.Background(), &query) require.Nil(t, err) require.Len(t, queryResult.Users, 1) require.EqualValues(t, queryResult.TotalCount, 1) // Custom ordering sortOpts, err := sortopts.ParseSortQueryParam("login-asc,email-asc") require.NoError(t, err) query = user.SearchUsersQuery{Query: "", Page: 1, Limit: 3, SignedInUser: usr, SortOpts: sortOpts} queryResult, err = userStore.Search(context.Background(), &query) require.Nil(t, err) require.Len(t, queryResult.Users, 3) require.EqualValues(t, queryResult.TotalCount, 5) for i := 0; i < 3; i++ { require.Equal(t, fmt.Sprint("loginuser", i), queryResult.Users[i].Login) } sortOpts2, err := sortopts.ParseSortQueryParam("login-desc,email-asc") require.NoError(t, err) query = user.SearchUsersQuery{Query: "", Page: 1, Limit: 3, SignedInUser: usr, SortOpts: sortOpts2} queryResult, err = userStore.Search(context.Background(), &query) require.Nil(t, err) require.Len(t, queryResult.Users, 3) require.EqualValues(t, queryResult.TotalCount, 5) for i := 0; i < 3; i++ { require.Equal(t, fmt.Sprint("loginuser", 4-i), queryResult.Users[i].Login) } }) t.Run("Can get logged in user projection", func(t *testing.T) { query := user.GetSignedInUserQuery{UserID: 2} queryResult, err := userStore.GetSignedInUser(context.Background(), &query) require.NoError(t, err) assert.Equal(t, queryResult.Email, "user1@test.com") assert.EqualValues(t, queryResult.OrgID, 2) assert.Equal(t, queryResult.Name, "user1") assert.Equal(t, queryResult.Login, "loginuser1") assert.EqualValues(t, queryResult.OrgRole, "Admin") assert.Equal(t, queryResult.OrgName, "user1@test.com") assert.Equal(t, queryResult.IsGrafanaAdmin, false) }) } func TestIntegrationUserUpdate(t *testing.T) { if testing.Short() { t.Skip("skipping integration test") } ss := db.InitTestDB(t) userStore := ProvideStore(ss, setting.NewCfg()) _, usrSvc := createOrgAndUserSvc(t, ss, ss.Cfg) users := createFiveTestUsers(t, usrSvc, func(i int) *user.CreateUserCommand { return &user.CreateUserCommand{ Email: fmt.Sprint("USER", i, "@test.com"), Name: fmt.Sprint("USER", i), Login: fmt.Sprint("loginUSER", i), IsDisabled: false, } }) userStore.cfg.CaseInsensitiveLogin = true t.Run("Testing DB - update generates duplicate user", func(t *testing.T) { err := userStore.Update(context.Background(), &user.UpdateUserCommand{ Login: "loginuser2", UserID: users[0].ID, }) require.Error(t, err) }) t.Run("Testing DB - update lowercases existing user", func(t *testing.T) { err := userStore.Update(context.Background(), &user.UpdateUserCommand{ Login: "loginUSER0", Email: "USER0@test.com", UserID: users[0].ID, }) require.NoError(t, err) result, err := userStore.GetByID(context.Background(), users[0].ID) require.NoError(t, err) require.Equal(t, "loginuser0", result.Login) require.Equal(t, "user0@test.com", result.Email) }) t.Run("Testing DB - no user info provided", func(t *testing.T) { err := userStore.Update(context.Background(), &user.UpdateUserCommand{ Login: "", Email: "", Name: "Change Name", UserID: users[3].ID, }) require.NoError(t, err) // query := user.GetUserByIDQuery{ID: users[3].ID} result, err := userStore.GetByID(context.Background(), users[3].ID) require.NoError(t, err) // Changed require.Equal(t, "Change Name", result.Name) // Unchanged require.Equal(t, "loginUSER3", result.Login) require.Equal(t, "USER3@test.com", result.Email) }) ss.Cfg.CaseInsensitiveLogin = false } func createFiveTestUsers(t *testing.T, svc user.Service, fn func(i int) *user.CreateUserCommand) []user.User { t.Helper() users := make([]user.User, 5) for i := 0; i < 5; i++ { cmd := fn(i) user, err := svc.Create(context.Background(), cmd) require.Nil(t, err) users[i] = *user } return users } func TestMetricsUsage(t *testing.T) { ss := db.InitTestDB(t) userStore := ProvideStore(ss, setting.NewCfg()) quotaService := quotaimpl.ProvideService(ss, ss.Cfg) orgService, err := orgimpl.ProvideService(ss, ss.Cfg, quotaService) require.NoError(t, err) _, usrSvc := createOrgAndUserSvc(t, ss, ss.Cfg) t.Run("Get empty role metrics for an org", func(t *testing.T) { orgId := int64(1) // create first user createFirtUserCmd := &user.CreateUserCommand{ Login: "admin", Email: "admin@admin.com", Name: "admin", OrgID: orgId, } _, err := usrSvc.Create(context.Background(), createFirtUserCmd) require.NoError(t, err) // create second user createSecondUserCmd := &user.CreateUserCommand{ Login: "userWithoutRole", Email: "userWithoutRole@userWithoutRole.com", Name: "userWithoutRole", } secondUser, err := usrSvc.Create(context.Background(), createSecondUserCmd) require.NoError(t, err) // assign the user to the org cmd := org.AddOrgUserCommand{ OrgID: secondUser.OrgID, UserID: orgId, Role: org.RoleNone, } err = orgService.AddOrgUser(context.Background(), &cmd) require.NoError(t, err) // get metric usage stats, err := userStore.CountUserAccountsWithEmptyRole(context.Background()) require.NoError(t, err) assert.Equal(t, int64(1), stats) }) } func createOrgAndUserSvc(t *testing.T, store db.DB, cfg *setting.Cfg) (org.Service, user.Service) { t.Helper() quotaService := quotaimpl.ProvideService(store, cfg) orgService, err := orgimpl.ProvideService(store, cfg, quotaService) require.NoError(t, err) usrSvc, err := ProvideService(store, orgService, cfg, nil, nil, quotaService, supportbundlestest.NewFakeBundleService()) require.NoError(t, err) return orgService, usrSvc }