X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/100710416212448cf653a260f9a67933a30656cf..10b08e0c05d176775c4f0cf13f6e77025bb1e636:/tools/sync-users/sync-users_test.go diff --git a/tools/sync-users/sync-users_test.go b/tools/sync-users/sync-users_test.go index 8b93d32e4d..119564d4df 100644 --- a/tools/sync-users/sync-users_test.go +++ b/tools/sync-users/sync-users_test.go @@ -98,6 +98,14 @@ func (s *TestSuite) TestParseFlagsWithoutPositionalArgument(c *C) { os.Args = []string{"cmd", "-verbose"} err := ParseFlags(&ConfigParams{}) c.Assert(err, NotNil) + c.Assert(err, ErrorMatches, ".*please provide a path to an input file.*") +} + +func (s *TestSuite) TestParseFlagsWrongUserID(c *C) { + os.Args = []string{"cmd", "-user-id", "nickname", "/tmp/somefile.csv"} + err := ParseFlags(&ConfigParams{}) + c.Assert(err, NotNil) + c.Assert(err, ErrorMatches, ".*user ID must be one of:.*") } func (s *TestSuite) TestParseFlagsWithPositionalArgument(c *C) { @@ -108,16 +116,20 @@ func (s *TestSuite) TestParseFlagsWithPositionalArgument(c *C) { c.Assert(cfg.Path, Equals, "/tmp/somefile.csv") c.Assert(cfg.Verbose, Equals, false) c.Assert(cfg.DeactivateUnlisted, Equals, false) + c.Assert(cfg.UserID, Equals, "email") + c.Assert(cfg.CaseInsensitive, Equals, true) } func (s *TestSuite) TestParseFlagsWithOptionalFlags(c *C) { cfg := ConfigParams{} - os.Args = []string{"cmd", "-verbose", "-deactivate-unlisted", "/tmp/somefile.csv"} + os.Args = []string{"cmd", "-verbose", "-deactivate-unlisted", "-user-id", "username", "/tmp/somefile.csv"} err := ParseFlags(&cfg) c.Assert(err, IsNil) c.Assert(cfg.Path, Equals, "/tmp/somefile.csv") c.Assert(cfg.Verbose, Equals, true) c.Assert(cfg.DeactivateUnlisted, Equals, true) + c.Assert(cfg.UserID, Equals, "username") + c.Assert(cfg.CaseInsensitive, Equals, false) } func (s *TestSuite) TestGetConfig(c *C) { @@ -218,90 +230,143 @@ func (s *TestSuite) TestWrongDataFields(c *C) { } } -// Activate and deactivate users +// Create, activate and deactivate users func (s *TestSuite) TestUserCreationAndUpdate(c *C) { - testCases := []userRecord{{ - UserID: "user1@example.com", - FirstName: "Example", - LastName: "User1", - Active: true, - Admin: false, - }, { - UserID: "admin1@example.com", - FirstName: "Example", - LastName: "Admin1", - Active: true, - Admin: true, - }} - // Make sure users aren't already there from fixtures - for _, user := range s.users { - e := user.Email - found := false - for _, r := range testCases { - if e == r.UserID { - found = true - break + for _, tc := range []string{"email", "username"} { + uIDPrefix := tc + uIDSuffix := "" + if tc == "email" { + uIDSuffix = "@example.com" + } + s.cfg.UserID = tc + records := []userRecord{{ + UserID: fmt.Sprintf("%suser1%s", uIDPrefix, uIDSuffix), + FirstName: "Example", + LastName: "User1", + Active: true, + Admin: false, + }, { + UserID: fmt.Sprintf("%suser2%s", uIDPrefix, uIDSuffix), + FirstName: "Example", + LastName: "User2", + Active: false, // initially inactive + Admin: false, + }, { + UserID: fmt.Sprintf("%sadmin1%s", uIDPrefix, uIDSuffix), + FirstName: "Example", + LastName: "Admin1", + Active: true, + Admin: true, + }, { + UserID: fmt.Sprintf("%sadmin2%s", uIDPrefix, uIDSuffix), + FirstName: "Example", + LastName: "Admin2", + Active: false, // initially inactive + Admin: true, + }} + // Make sure users aren't already there from fixtures + for _, user := range s.users { + uID, err := GetUserID(user, s.cfg.UserID) + c.Assert(err, IsNil) + found := false + for _, r := range records { + if uID == r.UserID { + found = true + break + } } + c.Assert(found, Equals, false) } - c.Assert(found, Equals, false) - } - // User creation - tmpfile, err := MakeTempCSVFile(RecordsToStrings(testCases)) - c.Assert(err, IsNil) - defer os.Remove(tmpfile.Name()) - s.cfg.Path = tmpfile.Name() - err = doMain(s.cfg) - c.Assert(err, IsNil) + // User creation + tmpfile, err := MakeTempCSVFile(RecordsToStrings(records)) + c.Assert(err, IsNil) + defer os.Remove(tmpfile.Name()) + s.cfg.Path = tmpfile.Name() + err = doMain(s.cfg) + c.Assert(err, IsNil) - users, err := ListUsers(s.cfg.Client) - c.Assert(err, IsNil) - for _, tc := range testCases { - var foundUser arvados.User - for _, user := range users { - if user.Email == tc.UserID { - foundUser = user - break + users, err := ListUsers(s.cfg.Client) + c.Assert(err, IsNil) + for _, r := range records { + var foundUser arvados.User + for _, user := range users { + uID, err := GetUserID(user, s.cfg.UserID) + c.Assert(err, IsNil) + if uID == r.UserID { + // Add an @example.com email if missing + // (to avoid database reset errors) + if tc == "username" && user.Email == "" { + err := UpdateUser(s.cfg.Client, user.UUID, &user, map[string]string{ + "email": fmt.Sprintf("%s@example.com", user.Username), + }) + c.Assert(err, IsNil) + } + foundUser = user + break + } } + c.Assert(foundUser, NotNil) + c.Logf("Checking creation for user %q", r.UserID) + c.Assert(foundUser.FirstName, Equals, r.FirstName) + c.Assert(foundUser.LastName, Equals, r.LastName) + c.Assert(foundUser.IsActive, Equals, r.Active) + c.Assert(foundUser.IsAdmin, Equals, (r.Active && r.Admin)) } - c.Assert(foundUser, NotNil) - c.Logf("Checking recently created user %q", foundUser.Email) - c.Assert(foundUser.FirstName, Equals, tc.FirstName) - c.Assert(foundUser.LastName, Equals, tc.LastName) - c.Assert(foundUser.IsActive, Equals, true) - c.Assert(foundUser.IsAdmin, Equals, tc.Admin) - } - // User deactivation - for idx := range testCases { - testCases[idx].Active = false - } - tmpfile, err = MakeTempCSVFile(RecordsToStrings(testCases)) - c.Assert(err, IsNil) - defer os.Remove(tmpfile.Name()) - s.cfg.Path = tmpfile.Name() - err = doMain(s.cfg) - c.Assert(err, IsNil) + // User update + for idx := range records { + records[idx].Active = !records[idx].Active + records[idx].FirstName = records[idx].FirstName + "Updated" + records[idx].LastName = records[idx].LastName + "Updated" + } + tmpfile, err = MakeTempCSVFile(RecordsToStrings(records)) + c.Assert(err, IsNil) + defer os.Remove(tmpfile.Name()) + s.cfg.Path = tmpfile.Name() + err = doMain(s.cfg) + c.Assert(err, IsNil) - users, err = ListUsers(s.cfg.Client) - c.Assert(err, IsNil) - for _, tc := range testCases { - var foundUser arvados.User - for _, user := range users { - if user.Email == tc.UserID { - foundUser = user - break + users, err = ListUsers(s.cfg.Client) + c.Assert(err, IsNil) + for _, r := range records { + var foundUser arvados.User + for _, user := range users { + uID, err := GetUserID(user, s.cfg.UserID) + c.Assert(err, IsNil) + if uID == r.UserID { + foundUser = user + break + } } + c.Assert(foundUser, NotNil) + c.Logf("Checking update for user %q", r.UserID) + c.Assert(foundUser.FirstName, Equals, r.FirstName) + c.Assert(foundUser.LastName, Equals, r.LastName) + c.Assert(foundUser.IsActive, Equals, r.Active) + c.Assert(foundUser.IsAdmin, Equals, (r.Active && r.Admin)) } - c.Assert(foundUser, NotNil) - c.Logf("Checking recently deactivated user %q", foundUser.Email) - c.Assert(foundUser.FirstName, Equals, tc.FirstName) - c.Assert(foundUser.LastName, Equals, tc.LastName) - c.Assert(foundUser.IsActive, Equals, false) - c.Assert(foundUser.IsAdmin, Equals, false) // inactive users cannot be admins } } func (s *TestSuite) TestDeactivateUnlisted(c *C) { localUserUuidRegex := regexp.MustCompile(fmt.Sprintf("^%s-tpzed-[0-9a-z]{15}$", s.cfg.ClusterID)) + + var user1 arvados.User + for _, nr := range []int{1, 2} { + var newUser arvados.User + err := CreateUser(s.cfg.Client, &newUser, map[string]string{ + "email": fmt.Sprintf("user%d@example.com", nr), + "first_name": "Example", + "last_name": fmt.Sprintf("User%d", nr), + "is_active": "true", + "is_admin": "false", + }) + c.Assert(err, IsNil) + c.Assert(newUser.IsActive, Equals, true) + if nr == 1 { + user1 = newUser // for later confirmation + } + } + users, err := ListUsers(s.cfg.Client) c.Assert(err, IsNil) previouslyActiveUsers := 0 @@ -317,19 +382,21 @@ func (s *TestSuite) TestDeactivateUnlisted(c *C) { previouslyActiveUsers++ } } - // At least 3 active users: System root, Anonymous and the current user. - // Other active users should exist from fixture. + // Active users: System root, Anonymous, current user and the + // ones just created (other active users may exist from fixture). c.Logf("Initial active users count: %d", previouslyActiveUsers) - c.Assert(previouslyActiveUsers > 3, Equals, true) + c.Assert(previouslyActiveUsers > 5, Equals, true) - s.cfg.DeactivateUnlisted = true - s.cfg.Verbose = true + // Here we omit user2@example.com from the CSV file. data := [][]string{ - {"user1@example.com", "Example", "User1", "0", "0"}, + {"user1@example.com", "Example", "User1", "1", "0"}, } tmpfile, err := MakeTempCSVFile(data) c.Assert(err, IsNil) defer os.Remove(tmpfile.Name()) + + s.cfg.DeactivateUnlisted = true + s.cfg.Verbose = true s.cfg.Path = tmpfile.Name() err = doMain(s.cfg) c.Assert(err, IsNil) @@ -341,6 +408,7 @@ func (s *TestSuite) TestDeactivateUnlisted(c *C) { fmt.Sprintf("%s-tpzed-000000000000000", s.cfg.ClusterID): true, fmt.Sprintf("%s-tpzed-anonymouspublic", s.cfg.ClusterID): true, s.cfg.CurrentUser.UUID: true, + user1.UUID: true, } remainingActiveUUIDs := map[string]bool{} seenUserEmails := map[string]bool{} @@ -357,9 +425,10 @@ func (s *TestSuite) TestDeactivateUnlisted(c *C) { currentlyActiveUsers++ } } - // 3 active users remaining: System root, Anonymous and the current user. + // 4 active users remaining: System root, Anonymous, the current user + // and user1@example.com c.Logf("Active local users remaining: %v", remainingActiveUUIDs) - c.Assert(currentlyActiveUsers, Equals, 3) + c.Assert(currentlyActiveUsers, Equals, 4) } func (s *TestSuite) TestFailOnDuplicatedEmails(c *C) { @@ -387,3 +456,106 @@ func (s *TestSuite) TestFailOnDuplicatedEmails(c *C) { c.Assert(err, NotNil) c.Assert(err, ErrorMatches, "skipped.*duplicated email address.*") } + +func (s *TestSuite) TestFailOnEmptyUsernames(c *C) { + for i := range []int{1, 2} { + var user arvados.User + err := CreateUser(s.cfg.Client, &user, map[string]string{ + "email": fmt.Sprintf("johndoe%d@example.com", i), + "username": "", + "first_name": "John", + "last_name": "Doe", + "is_active": "true", + "is_admin": "false", + }) + c.Assert(err, IsNil) + c.Assert(user.Username, Equals, fmt.Sprintf("johndoe%d", i)) + if i == 1 { + err = UpdateUser(s.cfg.Client, user.UUID, &user, map[string]string{ + "username": "", + }) + c.Assert(err, IsNil) + c.Assert(user.Username, Equals, "") + } + } + + s.cfg.Verbose = true + data := [][]string{ + {"johndoe0", "John", "Doe", "0", "0"}, + } + tmpfile, err := MakeTempCSVFile(data) + c.Assert(err, IsNil) + defer os.Remove(tmpfile.Name()) + s.cfg.Path = tmpfile.Name() + s.cfg.UserID = "username" + err = doMain(s.cfg) + c.Assert(err, NotNil) + c.Assert(err, ErrorMatches, "skipped 1 user account.*with empty username.*") +} + +func (s *TestSuite) TestFailOnDupedUsernameAndCaseInsensitiveMatching(c *C) { + for _, i := range []int{1, 2} { + var user arvados.User + emailPrefix := "john" + if i == 1 { + emailPrefix = "JOHN" + } + err := CreateUser(s.cfg.Client, &user, map[string]string{ + "email": fmt.Sprintf("%sdoe@example.com", emailPrefix), + "username": "", + "first_name": "John", + "last_name": "Doe", + "is_active": "true", + "is_admin": "false", + }) + c.Assert(err, IsNil) + c.Assert(user.Username, Equals, fmt.Sprintf("%sdoe", emailPrefix)) + } + + s.cfg.Verbose = true + data := [][]string{ + {"johndoe", "John", "Doe", "0", "0"}, + } + tmpfile, err := MakeTempCSVFile(data) + c.Assert(err, IsNil) + defer os.Remove(tmpfile.Name()) + s.cfg.Path = tmpfile.Name() + s.cfg.UserID = "username" + s.cfg.CaseInsensitive = true + err = doMain(s.cfg) + c.Assert(err, NotNil) + c.Assert(err, ErrorMatches, "case insensitive collision for username.*between.*and.*") +} + +func (s *TestSuite) TestSuccessOnUsernameAndCaseSensitiveMatching(c *C) { + for _, i := range []int{1, 2} { + var user arvados.User + emailPrefix := "john" + if i == 1 { + emailPrefix = "JOHN" + } + err := CreateUser(s.cfg.Client, &user, map[string]string{ + "email": fmt.Sprintf("%sdoe@example.com", emailPrefix), + "username": "", + "first_name": "John", + "last_name": "Doe", + "is_active": "true", + "is_admin": "false", + }) + c.Assert(err, IsNil) + c.Assert(user.Username, Equals, fmt.Sprintf("%sdoe", emailPrefix)) + } + + s.cfg.Verbose = true + data := [][]string{ + {"johndoe", "John", "Doe", "0", "0"}, + } + tmpfile, err := MakeTempCSVFile(data) + c.Assert(err, IsNil) + defer os.Remove(tmpfile.Name()) + s.cfg.Path = tmpfile.Name() + s.cfg.UserID = "username" + s.cfg.CaseInsensitive = false + err = doMain(s.cfg) + c.Assert(err, IsNil) +}