mirror of
https://github.com/grafana/grafana.git
synced 2024-12-02 05:29:42 -06:00
e6e8351ee9
* Chore: Remove CreateUserForTests * Apply suggestion from code review
880 lines
27 KiB
Go
880 lines
27 KiB
Go
package commands
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"sort"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
"github.com/urfave/cli/v2"
|
|
|
|
"github.com/grafana/grafana/pkg/infra/db"
|
|
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
|
|
"github.com/grafana/grafana/pkg/services/org/orgimpl"
|
|
"github.com/grafana/grafana/pkg/services/quota/quotatest"
|
|
"github.com/grafana/grafana/pkg/services/sqlstore"
|
|
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
|
"github.com/grafana/grafana/pkg/services/supportbundles/supportbundlestest"
|
|
"github.com/grafana/grafana/pkg/services/team/teamimpl"
|
|
"github.com/grafana/grafana/pkg/services/user"
|
|
"github.com/grafana/grafana/pkg/services/user/userimpl"
|
|
"github.com/grafana/grafana/pkg/services/user/usertest"
|
|
"github.com/grafana/grafana/pkg/setting"
|
|
)
|
|
|
|
// "Skipping conflicting users test for mysql as it does make unique constraint case insensitive by default
|
|
const ignoredDatabase = migrator.MySQL
|
|
|
|
func TestBuildConflictBlock(t *testing.T) {
|
|
type testBuildConflictBlock struct {
|
|
desc string
|
|
users []user.User
|
|
expectedBlock string
|
|
wantDiscardedBlock string
|
|
wantConflictUser *ConflictingUser
|
|
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,
|
|
},
|
|
{
|
|
desc: "should get conflict_email true and conflict_login empty string",
|
|
users: []user.User{
|
|
{
|
|
Email: "conflict@email",
|
|
Login: "login",
|
|
OrgID: int64(testOrgID),
|
|
},
|
|
{
|
|
Email: "conflict@EMAIL",
|
|
Login: "plainlogin",
|
|
OrgID: int64(testOrgID),
|
|
},
|
|
},
|
|
expectedBlock: "conflict: conflict@email",
|
|
wantedNumberOfUsers: 2,
|
|
wantConflictUser: &ConflictingUser{ConflictEmail: "true", ConflictLogin: ""},
|
|
},
|
|
{
|
|
desc: "should get conflict_email empty string and conflict_login true",
|
|
users: []user.User{
|
|
{
|
|
Email: "regular@email",
|
|
Login: "CONFLICTLOGIN",
|
|
OrgID: int64(testOrgID),
|
|
},
|
|
{
|
|
Email: "regular-no-conflict@email",
|
|
Login: "conflictlogin",
|
|
OrgID: int64(testOrgID),
|
|
},
|
|
},
|
|
expectedBlock: "conflict: conflictlogin",
|
|
wantedNumberOfUsers: 2,
|
|
wantConflictUser: &ConflictingUser{ConflictEmail: "", ConflictLogin: "true"},
|
|
},
|
|
}
|
|
for _, tc := range testCases {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
// Restore after destructive operation
|
|
sqlStore := db.InitTestDB(t)
|
|
if sqlStore.GetDialect().DriverName() != ignoredDatabase {
|
|
userStore := userimpl.ProvideStore(sqlStore, sqlStore.Cfg)
|
|
for _, u := range tc.users {
|
|
u := user.User{
|
|
Email: u.Email,
|
|
Name: u.Name,
|
|
Login: u.Login,
|
|
OrgID: int64(testOrgID),
|
|
Created: time.Now(),
|
|
Updated: time.Now(),
|
|
}
|
|
// call user store instead of user service so as not to prevent conflicting users
|
|
_, err := userStore.Insert(context.Background(), &u)
|
|
require.NoError(t, err, u)
|
|
}
|
|
m, err := GetUsersWithConflictingEmailsOrLogins(&cli.Context{Context: context.Background()}, sqlStore)
|
|
require.NoError(t, err)
|
|
r := ConflictResolver{Store: sqlStore}
|
|
r.BuildConflictBlocks(m, fmt.Sprintf)
|
|
require.Equal(t, tc.wantedNumberOfUsers, len(r.Blocks[tc.expectedBlock]))
|
|
if tc.wantDiscardedBlock != "" {
|
|
require.Equal(t, true, r.DiscardedBlocks[tc.wantDiscardedBlock])
|
|
}
|
|
if tc.wantConflictUser != nil {
|
|
for _, u := range m {
|
|
require.Equal(t, tc.wantConflictUser.ConflictEmail, u.ConflictEmail)
|
|
require.Equal(t, tc.wantConflictUser.ConflictLogin, u.ConflictLogin)
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestBuildConflictBlockFromFileRepresentation(t *testing.T) {
|
|
type testBuildConflictBlock struct {
|
|
desc string
|
|
users []user.User
|
|
fileString string
|
|
expectedBlocks []string
|
|
expectedIdsInBlocks map[string][]string
|
|
}
|
|
testOrgID := 1
|
|
testCases := []testBuildConflictBlock{
|
|
{
|
|
desc: "should be able to parse the fileString containing the conflicts",
|
|
users: []user.User{
|
|
{
|
|
Email: "test",
|
|
Login: "test",
|
|
OrgID: int64(testOrgID),
|
|
},
|
|
{
|
|
Email: "TEST",
|
|
Login: "TEST",
|
|
OrgID: int64(testOrgID),
|
|
},
|
|
{
|
|
Email: "test2",
|
|
Login: "test2",
|
|
OrgID: int64(testOrgID),
|
|
},
|
|
{
|
|
Email: "TEST2",
|
|
Login: "TEST2",
|
|
OrgID: int64(testOrgID),
|
|
},
|
|
{
|
|
Email: "Test2",
|
|
Login: "Test2",
|
|
OrgID: int64(testOrgID),
|
|
},
|
|
},
|
|
fileString: `conflict: test
|
|
- id: 2, email: test, login: test, last_seen_at: 2012-09-19T08:31:20Z, auth_module: , conflict_email: true, conflict_login: true
|
|
+ id: 3, email: TEST, login: TEST, last_seen_at: 2012-09-19T08:31:29Z, auth_module: , conflict_email: true, conflict_login: true
|
|
conflict: test2
|
|
- id: 4, email: test2, login: test2, last_seen_at: 2012-09-19T08:31:41Z, auth_module: , conflict_email: true, conflict_login: true
|
|
+ id: 5, email: TEST2, login: TEST2, last_seen_at: 2012-09-19T08:31:51Z, auth_module: , conflict_email: true, conflict_login: true
|
|
- id: 6, email: Test2, login: Test2, last_seen_at: 2012-09-19T08:32:03Z, auth_module: , conflict_email: true, conflict_login: true`,
|
|
expectedBlocks: []string{"conflict: test", "conflict: test2"},
|
|
expectedIdsInBlocks: map[string][]string{"conflict: test": {"2", "3"}, "conflict: test2": {"4", "5", "6"}},
|
|
},
|
|
{
|
|
desc: "should be able to parse the fileString containing the conflicts 123",
|
|
users: []user.User{
|
|
{
|
|
Email: "saml-misi@example.org",
|
|
Login: "saml-misi",
|
|
OrgID: int64(testOrgID),
|
|
},
|
|
{
|
|
Email: "saml-misi@example",
|
|
Login: "saml-Misi",
|
|
OrgID: int64(testOrgID),
|
|
},
|
|
},
|
|
fileString: `conflict: saml-misi
|
|
+ id: 5, email: saml-misi@example.org, login: saml-misi, last_seen_at: 2022-09-22T12:00:49Z, auth_module: auth.saml, conflict_email: , conflict_login: true
|
|
- id: 15, email: saml-misi@example, login: saml-Misi, last_seen_at: 2012-09-26T11:31:32Z, auth_module: , conflict_email: , conflict_login: true`,
|
|
expectedBlocks: []string{"conflict: saml-misi"},
|
|
expectedIdsInBlocks: map[string][]string{"conflict: saml-misi": {"5", "15"}},
|
|
},
|
|
}
|
|
for _, tc := range testCases {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
// Restore after destructive operation
|
|
sqlStore := db.InitTestDB(t)
|
|
if sqlStore.GetDialect().DriverName() != ignoredDatabase {
|
|
userStore := userimpl.ProvideStore(sqlStore, sqlStore.Cfg)
|
|
for _, u := range tc.users {
|
|
u := user.User{
|
|
Email: u.Email,
|
|
Name: u.Name,
|
|
Login: u.Login,
|
|
OrgID: int64(testOrgID),
|
|
Created: time.Now(),
|
|
Updated: time.Now(),
|
|
}
|
|
// call user store instead of user service so as not to prevent conflicting users
|
|
_, err := userStore.Insert(context.Background(), &u)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
conflicts, err := GetUsersWithConflictingEmailsOrLogins(&cli.Context{Context: context.Background()}, sqlStore)
|
|
r := ConflictResolver{Users: conflicts, Store: sqlStore}
|
|
r.BuildConflictBlocks(conflicts, fmt.Sprintf)
|
|
require.NoError(t, err)
|
|
validErr := getValidConflictUsers(&r, []byte(tc.fileString))
|
|
require.NoError(t, validErr)
|
|
|
|
// test starts here
|
|
keys := make([]string, 0)
|
|
for k := range r.Blocks {
|
|
keys = append(keys, k)
|
|
}
|
|
sort.Strings(keys)
|
|
require.Equal(t, tc.expectedBlocks, keys)
|
|
|
|
// we want to validate the ids in the blocks
|
|
for _, block := range tc.expectedBlocks {
|
|
// checking for parsing of ids
|
|
conflictIds := []string{}
|
|
for _, u := range r.Blocks[block] {
|
|
conflictIds = append(conflictIds, u.ID)
|
|
}
|
|
require.Equal(t, tc.expectedIdsInBlocks[block], conflictIds)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
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,
|
|
},
|
|
{
|
|
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 := db.InitTestDB(t)
|
|
if sqlStore.GetDialect().DriverName() != ignoredDatabase {
|
|
userStore := userimpl.ProvideStore(sqlStore, sqlStore.Cfg)
|
|
for _, u := range tc.users {
|
|
u := user.User{
|
|
Email: u.Email,
|
|
Name: u.Name,
|
|
Login: u.Login,
|
|
OrgID: int64(testOrgID),
|
|
IsServiceAccount: u.IsServiceAccount,
|
|
Created: time.Now(),
|
|
Updated: time.Now(),
|
|
}
|
|
// call user store instead of user service so as not to prevent conflicting users
|
|
_, err := userStore.Insert(context.Background(), &u)
|
|
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 TestGenerateConflictingUsersFile(t *testing.T) {
|
|
type testGenerateConflictUsers struct {
|
|
desc string
|
|
users []user.User
|
|
expectedDiscardedBlock string
|
|
expectedBlocks []string
|
|
expectedEmailInBlocks map[string][]string
|
|
}
|
|
testOrgID := 1
|
|
testCases := []testGenerateConflictUsers{
|
|
{
|
|
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),
|
|
},
|
|
},
|
|
expectedBlocks: []string{"conflict: ldap-admin", "conflict: user_duplicate_test_login", "conflict: oauth-admin@example.org", "conflict: user2"},
|
|
expectedEmailInBlocks: map[string][]string{
|
|
"conflict: ldap-admin": {"ldap-admin", "xo"},
|
|
"conflict: user_duplicate_test_login": {"user1", "user2"},
|
|
"conflict: oauth-admin@example.org": {"oauth-admin@EXAMPLE.ORG", "oauth-admin@example.org"},
|
|
"conflict: user2": {"USER2", "user2"},
|
|
},
|
|
expectedDiscardedBlock: "conflict: user2",
|
|
},
|
|
{
|
|
desc: "should get only one block with 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),
|
|
},
|
|
},
|
|
expectedBlocks: []string{"conflict: ldap-editor"},
|
|
expectedEmailInBlocks: map[string][]string{"conflict: ldap-editor": {"ldap-editor", "LDAP-EDITOR", "No confli"}},
|
|
},
|
|
}
|
|
for _, tc := range testCases {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
// Restore after destructive operation
|
|
sqlStore := db.InitTestDB(t)
|
|
if sqlStore.GetDialect().DriverName() != ignoredDatabase {
|
|
userStore := userimpl.ProvideStore(sqlStore, sqlStore.Cfg)
|
|
for _, u := range tc.users {
|
|
cmd := user.User{
|
|
Email: u.Email,
|
|
Name: u.Name,
|
|
Login: u.Login,
|
|
OrgID: int64(testOrgID),
|
|
Created: time.Now(),
|
|
Updated: time.Now(),
|
|
}
|
|
// call user store instead of user service so as not to prevent conflicting users
|
|
_, err := userStore.Insert(context.Background(), &cmd)
|
|
require.NoError(t, err)
|
|
}
|
|
m, err := GetUsersWithConflictingEmailsOrLogins(&cli.Context{Context: context.Background()}, sqlStore)
|
|
require.NoError(t, err)
|
|
r := ConflictResolver{Store: sqlStore}
|
|
r.BuildConflictBlocks(m, fmt.Sprintf)
|
|
if tc.expectedDiscardedBlock != "" {
|
|
require.Equal(t, true, r.DiscardedBlocks[tc.expectedDiscardedBlock])
|
|
}
|
|
|
|
// test starts here
|
|
keys := make([]string, 0)
|
|
for k := range r.Blocks {
|
|
keys = append(keys, k)
|
|
}
|
|
expectedBlocks := tc.expectedBlocks
|
|
sort.Strings(keys)
|
|
sort.Strings(expectedBlocks)
|
|
require.Equal(t, expectedBlocks, keys)
|
|
|
|
// we want to validate the ids in the blocks
|
|
for _, block := range tc.expectedBlocks {
|
|
// checking for parsing of ids
|
|
conflictEmails := []string{}
|
|
for _, u := range r.Blocks[block] {
|
|
conflictEmails = append(conflictEmails, u.Email)
|
|
}
|
|
expectedEmailsInBlock := tc.expectedEmailInBlocks[block]
|
|
sort.Strings(conflictEmails)
|
|
sort.Strings(expectedEmailsInBlock)
|
|
require.Equal(t, expectedEmailsInBlock, conflictEmails)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRunValidateConflictUserFile(t *testing.T) {
|
|
t.Run("should validate file thats gets created", func(t *testing.T) {
|
|
// Restore after destructive operation
|
|
sqlStore := db.InitTestDB(t)
|
|
usrSvc := setupTestUserService(t, sqlStore)
|
|
|
|
const testOrgID int64 = 1
|
|
if sqlStore.GetDialect().DriverName() != ignoredDatabase {
|
|
// add additional user with conflicting login where DOMAIN is upper case
|
|
dupUserLogincmd := user.CreateUserCommand{
|
|
Email: "userduplicatetest1@test.com",
|
|
Login: "user_duplicate_test_1_login",
|
|
OrgID: testOrgID,
|
|
}
|
|
_, err := usrSvc.Create(context.Background(), &dupUserLogincmd)
|
|
require.NoError(t, err)
|
|
dupUserEmailcmd := user.CreateUserCommand{
|
|
Email: "USERDUPLICATETEST1@TEST.COM",
|
|
Login: "USER_DUPLICATE_TEST_1_LOGIN",
|
|
OrgID: testOrgID,
|
|
}
|
|
_, err = usrSvc.Create(context.Background(), &dupUserEmailcmd)
|
|
require.NoError(t, err)
|
|
|
|
// get users
|
|
conflictUsers, err := GetUsersWithConflictingEmailsOrLogins(&cli.Context{Context: context.Background()}, sqlStore)
|
|
require.NoError(t, err)
|
|
r := ConflictResolver{Store: sqlStore}
|
|
r.BuildConflictBlocks(conflictUsers, fmt.Sprintf)
|
|
tmpFile, err := generateConflictUsersFile(&r)
|
|
require.NoError(t, err)
|
|
|
|
b, err := os.ReadFile(tmpFile.Name())
|
|
require.NoError(t, err)
|
|
|
|
validErr := getValidConflictUsers(&r, b)
|
|
require.NoError(t, validErr)
|
|
require.Equal(t, 2, len(r.ValidUsers))
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestIntegrationMergeUser(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("skipping integration test")
|
|
}
|
|
t.Run("should be able to merge user", func(t *testing.T) {
|
|
// Restore after destructive operation
|
|
sqlStore := db.InitTestDB(t)
|
|
teamSvc := teamimpl.ProvideService(sqlStore, setting.NewCfg())
|
|
team1, err := teamSvc.CreateTeam("team1 name", "", 1)
|
|
require.Nil(t, err)
|
|
usrSvc := setupTestUserService(t, sqlStore)
|
|
const testOrgID int64 = 1
|
|
|
|
if sqlStore.GetDialect().DriverName() != ignoredDatabase {
|
|
// add additional user with conflicting login where DOMAIN is upper case
|
|
|
|
// the order of adding the conflict matters
|
|
dupUserLogincmd := user.CreateUserCommand{
|
|
Email: "userduplicatetest1@test.com",
|
|
Name: "user name 1",
|
|
Login: "user_duplicate_test_1_login",
|
|
OrgID: testOrgID,
|
|
}
|
|
_, err := usrSvc.Create(context.Background(), &dupUserLogincmd)
|
|
require.NoError(t, err)
|
|
dupUserEmailcmd := user.CreateUserCommand{
|
|
Email: "USERDUPLICATETEST1@TEST.COM",
|
|
Name: "user name 1",
|
|
Login: "USER_DUPLICATE_TEST_1_LOGIN",
|
|
OrgID: testOrgID,
|
|
}
|
|
userWithUpperCase, err := usrSvc.Create(context.Background(), &dupUserEmailcmd)
|
|
require.NoError(t, err)
|
|
// this is the user we want to update to another team
|
|
err = teamSvc.AddTeamMember(userWithUpperCase.ID, testOrgID, team1.ID, false, 0)
|
|
require.NoError(t, err)
|
|
|
|
// get users
|
|
conflictUsers, err := GetUsersWithConflictingEmailsOrLogins(&cli.Context{Context: context.Background()}, sqlStore)
|
|
require.NoError(t, err)
|
|
r := ConflictResolver{
|
|
Store: sqlStore,
|
|
userService: usertest.NewUserServiceFake(),
|
|
ac: actest.FakeService{},
|
|
}
|
|
r.BuildConflictBlocks(conflictUsers, fmt.Sprintf)
|
|
tmpFile, err := generateConflictUsersFile(&r)
|
|
require.NoError(t, err)
|
|
// validation to get newConflicts
|
|
// edited file
|
|
b, err := os.ReadFile(tmpFile.Name())
|
|
require.NoError(t, err)
|
|
validErr := getValidConflictUsers(&r, b)
|
|
require.NoError(t, validErr)
|
|
require.Equal(t, 2, len(r.ValidUsers))
|
|
|
|
// test starts here
|
|
err = r.MergeConflictingUsers(context.Background())
|
|
require.NoError(t, err)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestIntegrationMergeUserFromNewFileInput(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("skipping integration test")
|
|
}
|
|
t.Run("should be able to merge users after choosing a different user to keep", func(t *testing.T) {
|
|
type testBuildConflictBlock struct {
|
|
desc string
|
|
users []user.User
|
|
fileString string
|
|
expectedValidationErr error
|
|
expectedBlocks []string
|
|
expectedIdsInBlocks map[string][]string
|
|
}
|
|
testOrgID := 1
|
|
m := make(map[string][]string)
|
|
conflict1 := "conflict: test"
|
|
conflict2 := "conflict: test2"
|
|
m[conflict1] = []string{"2", "3"}
|
|
m[conflict2] = []string{"4", "5", "6"}
|
|
testCases := []testBuildConflictBlock{
|
|
{
|
|
desc: "should be able to parse the fileString containing the conflicts",
|
|
users: []user.User{
|
|
{
|
|
Email: "TEST",
|
|
Login: "TEST",
|
|
OrgID: int64(testOrgID),
|
|
},
|
|
{
|
|
Email: "test",
|
|
Login: "test",
|
|
OrgID: int64(testOrgID),
|
|
},
|
|
{
|
|
Email: "test2",
|
|
Login: "test2",
|
|
OrgID: int64(testOrgID),
|
|
},
|
|
{
|
|
Email: "TEST2",
|
|
Login: "TEST2",
|
|
OrgID: int64(testOrgID),
|
|
},
|
|
{
|
|
Email: "Test2",
|
|
Login: "Test2",
|
|
OrgID: int64(testOrgID),
|
|
},
|
|
},
|
|
fileString: `conflict: test
|
|
- id: 1, email: test, login: test, last_seen_at: 2012-09-19T08:31:20Z, auth_module:, conflict_email: true, conflict_login: true
|
|
+ id: 2, email: TEST, login: TEST, last_seen_at: 2012-09-19T08:31:29Z, auth_module:, conflict_email: true, conflict_login: true
|
|
conflict: test2
|
|
- id: 3, email: test2, login: test2, last_seen_at: 2012-09-19T08:31:41Z, auth_module: , conflict_email: true, conflict_login: true
|
|
+ id: 4, email: TEST2, login: TEST2, last_seen_at: 2012-09-19T08:31:51Z, auth_module: , conflict_email: true, conflict_login: true
|
|
- id: 5, email: Test2, login: Test2, last_seen_at: 2012-09-19T08:32:03Z, auth_module: , conflict_email: true, conflict_login: true`,
|
|
expectedBlocks: []string{"conflict: test", "conflict: test2"},
|
|
expectedIdsInBlocks: m,
|
|
},
|
|
{
|
|
desc: "should give error for having wrong number of users to keep",
|
|
users: []user.User{
|
|
{
|
|
Email: "TEST",
|
|
Login: "TEST",
|
|
OrgID: int64(testOrgID),
|
|
},
|
|
{
|
|
Email: "test",
|
|
Login: "test",
|
|
OrgID: int64(testOrgID),
|
|
},
|
|
},
|
|
fileString: `conflict: test
|
|
+ id: 1, email: test, login: test, last_seen_at: 2012-09-19T08:31:20Z, auth_module:, conflict_email: true, conflict_login: true
|
|
+ id: 2, email: TEST, login: TEST, last_seen_at: 2012-09-19T08:31:29Z, auth_module:, conflict_email: true, conflict_login: true
|
|
`,
|
|
expectedValidationErr: fmt.Errorf("invalid number of users to keep, expected 1, got 2 for block: conflict: test"),
|
|
expectedBlocks: []string{"conflict: test"},
|
|
},
|
|
{
|
|
desc: "should give error for having wrong character for user",
|
|
users: []user.User{
|
|
{
|
|
Email: "TEST",
|
|
Login: "TEST",
|
|
OrgID: int64(testOrgID),
|
|
},
|
|
{
|
|
Email: "test",
|
|
Login: "test",
|
|
OrgID: int64(testOrgID),
|
|
},
|
|
},
|
|
fileString: `conflict: test
|
|
+ id: 1, email: test, login: test, last_seen_at: 2012-09-19T08:31:20Z, auth_module:, conflict_email: true, conflict_login: true
|
|
% id: 2, email: TEST, login: TEST, last_seen_at: 2012-09-19T08:31:29Z, auth_module:, conflict_email: true, conflict_login: true
|
|
`,
|
|
expectedValidationErr: fmt.Errorf("invalid start character (expected '+,-') found %% for row number 3"),
|
|
expectedBlocks: []string{"conflict: test"},
|
|
},
|
|
}
|
|
for _, tc := range testCases {
|
|
// Restore after destructive operation
|
|
sqlStore := db.InitTestDB(t)
|
|
if sqlStore.GetDialect().DriverName() != ignoredDatabase {
|
|
userStore := userimpl.ProvideStore(sqlStore, sqlStore.Cfg)
|
|
for _, u := range tc.users {
|
|
cmd := user.User{
|
|
Email: u.Email,
|
|
Name: u.Name,
|
|
Login: u.Login,
|
|
OrgID: int64(testOrgID),
|
|
Created: time.Now(),
|
|
Updated: time.Now(),
|
|
}
|
|
// call user store instead of user service so as not to prevent conflicting users
|
|
_, err := userStore.Insert(context.Background(), &cmd)
|
|
require.NoError(t, err)
|
|
}
|
|
// add additional user with conflicting login where DOMAIN is upper case
|
|
conflictUsers, err := GetUsersWithConflictingEmailsOrLogins(&cli.Context{Context: context.Background()}, sqlStore)
|
|
require.NoError(t, err)
|
|
userFake := usertest.NewUserServiceFake()
|
|
userFake.ExpectedUser = &user.User{Email: "test", Login: "test", OrgID: int64(testOrgID)}
|
|
r := ConflictResolver{
|
|
Store: sqlStore,
|
|
userService: userFake,
|
|
ac: actest.FakeService{},
|
|
}
|
|
r.BuildConflictBlocks(conflictUsers, fmt.Sprintf)
|
|
require.NoError(t, err)
|
|
// validation to get newConflicts
|
|
// edited file
|
|
// b, err := os.ReadFile(tmpFile.Name())
|
|
// mocked file input
|
|
b := tc.fileString
|
|
require.NoError(t, err)
|
|
validErr := getValidConflictUsers(&r, []byte(b))
|
|
if tc.expectedValidationErr != nil {
|
|
require.Equal(t, tc.expectedValidationErr, validErr)
|
|
} else {
|
|
require.NoError(t, validErr)
|
|
}
|
|
|
|
// test starts here
|
|
err = r.MergeConflictingUsers(context.Background())
|
|
require.NoError(t, err)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestMarshalConflictUser(t *testing.T) {
|
|
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, last_seen_at: 2012-07-26T16:08:11Z, auth_module: auth.saml, conflict_email: true, conflict_login: ",
|
|
expectedUser: ConflictingUser{
|
|
Direction: "+",
|
|
ID: "4",
|
|
Email: "userduplicatetest1@test.com",
|
|
Login: "userduplicatetest1",
|
|
LastSeenAt: "2012-07-26T16:08:11Z",
|
|
AuthModule: "auth.saml",
|
|
ConflictEmail: "true",
|
|
ConflictLogin: "",
|
|
},
|
|
},
|
|
{
|
|
name: "should be able to marshal expected input row",
|
|
inputRow: "+ id: 1, email: userduplicatetest1@test.com, login: user_duplicate_test_1_login, last_seen_at: 2012-07-26T16:08:11Z, auth_module: , conflict_email: , conflict_login: true",
|
|
expectedUser: ConflictingUser{
|
|
Direction: "+",
|
|
ID: "1",
|
|
Email: "userduplicatetest1@test.com",
|
|
Login: "user_duplicate_test_1_login",
|
|
LastSeenAt: "2012-07-26T16:08:11Z",
|
|
AuthModule: "",
|
|
ConflictEmail: "",
|
|
ConflictLogin: "true",
|
|
},
|
|
},
|
|
}
|
|
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)
|
|
require.Equal(t, tc.expectedUser.ConflictEmail, user.ConflictEmail)
|
|
require.Equal(t, tc.expectedUser.ConflictLogin, user.ConflictLogin)
|
|
})
|
|
}
|
|
}
|
|
|
|
func setupTestUserService(t *testing.T, sqlStore *sqlstore.SQLStore) user.Service {
|
|
t.Helper()
|
|
orgSvc, err := orgimpl.ProvideService(sqlStore, sqlStore.Cfg, "atest.FakeQuotaService{})
|
|
require.NoError(t, err)
|
|
usrSvc, err := userimpl.ProvideService(sqlStore, orgSvc, sqlStore.Cfg, nil, nil, "atest.FakeQuotaService{}, supportbundlestest.NewFakeBundleService())
|
|
require.NoError(t, err)
|
|
|
|
return usrSvc
|
|
}
|