Files
grafana/pkg/cmd/grafana-cli/commands/conflict_user_command_test.go
Eric Leijonmarck cac3833b8e Auth: Add cli command users-manager for conflict resolution (#52344)
* add users-manager command

* add users-manager command

* rename files

* refactor: imports and renaming

* Command: add conflict merge user command

- MergeUser will
	- replace all user_ids from conflicting users to the chosen userId
	- delete users whose user_ids are not the chosen user
- SameIdentification will
	- update chosen user with chosen email,login details
	- delete users whose user_ids are not the chosen user

* refactor: clean up

* refactor: create structure for read, validate, ingest

* feat: ls and generate-file for conflicting users

* remove usagestats

* added back pkg/services/login/authinfoservice/database/stats.go

* Revert "added back pkg/services/login/authinfoservice/database/stats.go"

This reverts commit 2ba6e3c4d6.

* Revert "remove usagestats"

This reverts commit 1e3fa97810.

* cherry pick

* Revert "cherry pick"

This reverts commit 461626c306.

* fix test

* make lint

* make test run

* clean up and refactored to align with downstream refactoring

* formatting

* refactor: name list instead of ls

* fix: static lint error use trimprefix

* fix: remove unused functions in sqlstore

* fix: remove unused function

* handling of multiple users and resolve discarded users

* fix tests

* fix: bug that did not exclude the blocks

* ioutil is blacklisted

* should not run tests for mysql

* fix tests
2022-08-12 14:47:31 +01:00

385 lines
9.4 KiB
Go

package commands
import (
"context"
"fmt"
"testing"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/user"
"github.com/stretchr/testify/require"
"github.com/urfave/cli/v2"
)
func TestGetConflictingUsers(t *testing.T) {
type testListConflictingUsers struct {
desc string
users []user.User
want int
wantErr error
}
testOrgID := 1
testCases := []testListConflictingUsers{
{
desc: "should get login conflicting users",
users: []user.User{
{
Email: "xo",
Login: "ldap-admin",
OrgID: int64(testOrgID),
},
{
Email: "ldap-admin",
Login: "LDAP-ADMIN",
OrgID: int64(testOrgID),
},
},
want: 2,
},
{
desc: "should get email conflicting users",
users: []user.User{
{
Email: "oauth-admin@example.org",
Login: "No confli",
OrgID: int64(testOrgID),
},
{
Email: "oauth-admin@EXAMPLE.ORG",
Login: "oauth-admin",
OrgID: int64(testOrgID),
},
},
want: 2,
},
// TODO:
// refactor the sql to get 3 users from this test
// if this is changed, one needs to correct the filerepresentation
{
desc: "should be 5 conflicting users, each conflict gets 2 users",
users: []user.User{
{
Email: "user1",
Login: "USER_DUPLICATE_TEST_LOGIN",
OrgID: int64(testOrgID),
},
{
Email: "user2",
Login: "user_duplicate_test_login",
OrgID: int64(testOrgID),
},
{
Email: "USER2",
Login: "no-conflict-login",
OrgID: int64(testOrgID),
},
{
Email: "no-conflict",
Login: "user_DUPLICATE_test_login",
OrgID: int64(testOrgID),
},
},
want: 5,
},
{
desc: "should be 8 conflicting users, each conflict gets 2 users",
users: []user.User{
{
Email: "user1",
Login: "USER_DUPLICATE_TEST_LOGIN",
OrgID: int64(testOrgID),
},
{
Email: "user2",
Login: "user_duplicate_test_login",
OrgID: int64(testOrgID),
},
{
Email: "USER2",
Login: "no-conflict-login",
OrgID: int64(testOrgID),
},
{
Email: "xo",
Login: "ldap-admin",
OrgID: int64(testOrgID),
},
{
Email: "ldap-admin",
Login: "LDAP-ADMIN",
OrgID: int64(testOrgID),
},
{
Email: "oauth-admin@example.org",
Login: "No confli",
OrgID: int64(testOrgID),
},
{
Email: "oauth-admin@EXAMPLE.ORG",
Login: "oauth-admin",
OrgID: int64(testOrgID),
},
},
want: 8,
},
{
desc: "should not get service accounts",
users: []user.User{
{
Email: "sa-x",
Login: "sa-x",
OrgID: int64(testOrgID),
IsServiceAccount: true,
},
{
Email: "sa-X",
Login: "sa-X",
OrgID: int64(testOrgID),
IsServiceAccount: true,
},
},
want: 0,
},
{
desc: "should get nil when no users in database",
users: []user.User{},
want: 0,
wantErr: nil,
},
}
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
// Restore after destructive operation
sqlStore := sqlstore.InitTestDB(t)
// "Skipping conflicting users test for mysql as it does make unique constraint case insensitive by default
if sqlStore.GetDialect().DriverName() != "mysql" {
for _, u := range tc.users {
cmd := user.CreateUserCommand{
Email: u.Email,
Name: u.Name,
Login: u.Login,
OrgID: int64(testOrgID),
IsServiceAccount: u.IsServiceAccount,
}
_, err := sqlStore.CreateUser(context.Background(), cmd)
require.NoError(t, err)
}
m, err := GetUsersWithConflictingEmailsOrLogins(&cli.Context{Context: context.Background()}, sqlStore)
require.NoError(t, err)
require.Equal(t, tc.want, len(m))
if tc.wantErr != nil {
require.EqualError(t, err, tc.wantErr.Error())
}
}
})
}
}
func TestBuildConflictBlock(t *testing.T) {
type testBuildConflictBlock struct {
desc string
users []user.User
expectedBlock string
wantDiscardedBlock string
wantedNumberOfUsers int
}
testOrgID := 1
testCases := []testBuildConflictBlock{
{
desc: "should get one block with only 3 users",
users: []user.User{
{
Email: "ldap-editor",
Login: "ldap-editor",
OrgID: int64(testOrgID),
},
{
Email: "LDAP-EDITOR",
Login: "LDAP-EDITOR",
OrgID: int64(testOrgID),
},
{
Email: "overlapping conflict",
Login: "LDAP-editor",
OrgID: int64(testOrgID),
},
{
Email: "OVERLAPPING conflict",
Login: "no conflict",
OrgID: int64(testOrgID),
},
},
expectedBlock: "conflict: ldap-editor",
wantDiscardedBlock: "conflict: overlapping conflict",
wantedNumberOfUsers: 3,
},
}
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
// Restore after destructive operation
sqlStore := sqlstore.InitTestDB(t)
// "Skipping conflicting users test for mysql as it does make unique constraint case insensitive by default
if sqlStore.GetDialect().DriverName() != "mysql" {
for _, u := range tc.users {
cmd := user.CreateUserCommand{
Email: u.Email,
Name: u.Name,
Login: u.Login,
OrgID: int64(testOrgID),
}
_, err := sqlStore.CreateUser(context.Background(), cmd)
require.NoError(t, err)
}
m, err := GetUsersWithConflictingEmailsOrLogins(&cli.Context{Context: context.Background()}, sqlStore)
require.NoError(t, err)
r := ConflictResolver{Users: m}
r.BuildConflictBlocks(fmt.Sprintf)
require.Equal(t, tc.wantedNumberOfUsers, len(r.Blocks[tc.expectedBlock]))
require.Equal(t, true, r.DiscardedBlocks[tc.wantDiscardedBlock])
}
})
}
}
func TestGenerateConflictingUsersFile(t *testing.T) {
type testListConflictingUsers struct {
desc string
users []user.User
wantDiscardedBlock string
want string
}
testOrgID := 1
testCases := []testListConflictingUsers{
{
desc: "should get conflicting users",
users: []user.User{
{
Email: "user1",
Login: "USER_DUPLICATE_TEST_LOGIN",
OrgID: int64(testOrgID),
},
{
Email: "user2",
Login: "user_duplicate_test_login",
OrgID: int64(testOrgID),
},
{
Email: "USER2",
Login: "no-conflict-login",
OrgID: int64(testOrgID),
},
{
Email: "xo",
Login: "ldap-admin",
OrgID: int64(testOrgID),
},
{
Email: "ldap-admin",
Login: "LDAP-ADMIN",
OrgID: int64(testOrgID),
},
{
Email: "oauth-admin@example.org",
Login: "No conflict",
OrgID: int64(testOrgID),
},
{
Email: "oauth-admin@EXAMPLE.ORG",
Login: "oauth-admin",
OrgID: int64(testOrgID),
},
},
wantDiscardedBlock: "conflict: user2",
},
{
desc: "should get one block with only 3 users",
users: []user.User{
{
Email: "ldap-editor",
Login: "ldap-editor",
OrgID: int64(testOrgID),
},
{
Email: "LDAP-EDITOR",
Login: "LDAP-EDITOR",
OrgID: int64(testOrgID),
},
{
Email: "No confli",
Login: "LDAP-editor",
OrgID: int64(testOrgID),
},
},
want: `conflict: ldap-editor
+ id: 1, email: ldap-editor, login: ldap-editor
- id: 2, email: LDAP-EDITOR, login: LDAP-EDITOR
- id: 3, email: No confli, login: LDAP-editor
`,
},
}
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
// Restore after destructive operation
sqlStore := sqlstore.InitTestDB(t)
// "Skipping conflicting users test for mysql as it does make unique constraint case insensitive by default
if sqlStore.GetDialect().DriverName() != "mysql" {
for _, u := range tc.users {
cmd := user.CreateUserCommand{
Email: u.Email,
Name: u.Name,
Login: u.Login,
OrgID: int64(testOrgID),
}
_, err := sqlStore.CreateUser(context.Background(), cmd)
require.NoError(t, err)
}
m, err := GetUsersWithConflictingEmailsOrLogins(&cli.Context{Context: context.Background()}, sqlStore)
require.NoError(t, err)
r := ConflictResolver{Users: m}
r.BuildConflictBlocks(fmt.Sprintf)
if tc.wantDiscardedBlock != "" {
require.Equal(t, true, r.DiscardedBlocks[tc.wantDiscardedBlock])
}
if tc.want != "" {
fileString := r.ToStringPresentation()
require.Equal(t, tc.want, fileString)
}
}
})
}
}
func TestMarshalConflictUser(t *testing.T) {
// TODO: add more testcases
testCases := []struct {
name string
inputRow string
expectedUser ConflictingUser
}{{
name: "should be able to marshal expected input row",
inputRow: "+ id: 4, email: userduplicatetest1@test.com, login: userduplicatetest1@test.com, last_seen_at: 2012-07-26T16:08:11Z, auth_module:",
expectedUser: ConflictingUser{
Direction: "+",
Id: "4",
Email: "userduplicatetest1@test.com",
Login: "userduplicatetest1@test.com",
LastSeenAt: "2012-07-26T16:08:11Z",
AuthModule: "",
},
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
user := ConflictingUser{}
err := user.Marshal(tc.inputRow)
require.NoError(t, err)
require.Equal(t, tc.expectedUser.Direction, user.Direction)
require.Equal(t, tc.expectedUser.Id, user.Id)
require.Equal(t, tc.expectedUser.Email, user.Email)
require.Equal(t, tc.expectedUser.Login, user.Login)
require.Equal(t, tc.expectedUser.LastSeenAt, user.LastSeenAt)
})
}
}