From 4e355db55f12be1944a1e21b9f386e6bc101dcde Mon Sep 17 00:00:00 2001 From: Lucas Di Pentima Date: Fri, 17 Jun 2022 15:58:38 -0300 Subject: [PATCH] 18858: Avoids updating the current user. Adds unlisted user disable option. Also, adds verbose logging. Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima --- tools/sync-users/.gitignore | 1 + tools/sync-users/sync-users.go | 76 ++++++++++++++++++++++++++++++---- 2 files changed, 69 insertions(+), 8 deletions(-) create mode 100644 tools/sync-users/.gitignore diff --git a/tools/sync-users/.gitignore b/tools/sync-users/.gitignore new file mode 100644 index 0000000000..cbbc17612d --- /dev/null +++ b/tools/sync-users/.gitignore @@ -0,0 +1 @@ +sync-users \ No newline at end of file diff --git a/tools/sync-users/sync-users.go b/tools/sync-users/sync-users.go index 28d7e73735..7cb55f74ea 100644 --- a/tools/sync-users/sync-users.go +++ b/tools/sync-users/sync-users.go @@ -58,9 +58,11 @@ func main() { } type ConfigParams struct { - Path string - Verbose bool - Client *arvados.Client + Client *arvados.Client + CurrentUser arvados.User + DeactivateUnlisted bool + Path string + Verbose bool } func ParseFlags(cfg *ConfigParams) error { @@ -78,6 +80,10 @@ func ParseFlags(cfg *ConfigParams) error { flags.PrintDefaults() } + deactivateUnlisted := flags.Bool( + "deactivate-unlisted", + false, + "Deactivate users that are not in the input file.") verbose := flags.Bool( "verbose", false, @@ -105,6 +111,7 @@ func ParseFlags(cfg *ConfigParams) error { return fmt.Errorf("input file path invalid") } + cfg.DeactivateUnlisted = *deactivateUnlisted cfg.Path = *srcPath cfg.Verbose = *verbose @@ -126,8 +133,12 @@ func GetConfig() (cfg ConfigParams, err error) { return cfg, fmt.Errorf("error getting the current user: %s", err) } if !u.IsAdmin { - return cfg, fmt.Errorf("current user (%s) is not an admin user", u.UUID) + return cfg, fmt.Errorf("current user %q is not an admin user", u.UUID) + } + if cfg.Verbose { + log.Printf("Running as admin user %q", u.UUID) } + cfg.CurrentUser = u return cfg, nil } @@ -141,6 +152,7 @@ func doMain(cfg *ConfigParams) error { defer f.Close() allUsers := make(map[string]arvados.User) + processedUsers := make(map[string]bool) results, err := GetAll(cfg.Client, "users", arvados.ResourceListParams{}, &UserList{}) if err != nil { return fmt.Errorf("error getting all users: %s", err) @@ -149,6 +161,7 @@ func doMain(cfg *ConfigParams) error { for _, item := range results { u := item.(arvados.User) allUsers[strings.ToLower(u.Email)] = u + processedUsers[strings.ToLower(u.Email)] = false } loadedRecords, err := LoadInputFile(f) @@ -159,14 +172,42 @@ func doMain(cfg *ConfigParams) error { updatesSucceeded, updatesFailed := 0, 0 for _, record := range loadedRecords { + if record.Email == cfg.CurrentUser.Email { + log.Printf("Skipping current user %q from processing", record.Email) + continue + } if updated, err := ProcessRecord(cfg, record, allUsers); err != nil { log.Printf("error processing record %q: %s", record.Email, err) updatesFailed++ } else if updated { + processedUsers[strings.ToLower(record.Email)] = true updatesSucceeded++ } } - log.Printf("Updated %d account(s), failed to update %d account(s)", updatesSucceeded, updatesFailed) + + if cfg.DeactivateUnlisted { + for email, user := range allUsers { + if user.UUID == cfg.CurrentUser.UUID { + log.Printf("Skipping current user deactivation: %s", user.UUID) + continue + } + if !processedUsers[email] { + if cfg.Verbose { + log.Printf("Deactivating unlisted user %q", user.UUID) + } + var updatedUser arvados.User + if err := UnsetupUser(cfg.Client, user.UUID, &updatedUser); err != nil { + log.Printf("error deactivating unlisted user %q: %s", user.UUID, err) + updatesFailed++ + } else { + allUsers[email] = updatedUser + updatesSucceeded++ + } + } + } + } + + log.Printf("Updated %d user(s), failed to update %d user(s)", updatesSucceeded, updatesFailed) return nil } @@ -181,13 +222,22 @@ type userRecord struct { // ProcessRecord creates or updates a user based on the given record func ProcessRecord(cfg *ConfigParams, record userRecord, allUsers map[string]arvados.User) (bool, error) { + if cfg.Verbose { + log.Printf("Processing record for user %q", record.Email) + } + wantedActiveStatus := strconv.FormatBool(record.Active) wantedAdminStatus := strconv.FormatBool(record.Admin) + createRequired := false updateRequired := false // Check if user exists, set its active & admin status. var user arvados.User user, ok := allUsers[record.Email] if !ok { + if cfg.Verbose { + log.Printf("User %q does not exist, creating", record.Email) + } + createRequired = true err := CreateUser(cfg.Client, &user, map[string]string{ "email": record.Email, "first_name": record.FirstName, @@ -198,12 +248,13 @@ func ProcessRecord(cfg *ConfigParams, record userRecord, allUsers map[string]arv if err != nil { return false, fmt.Errorf("error creating user %q: %s", record.Email, err) } - updateRequired = true - log.Printf("Created user %q", record.Email) } if record.Active != user.IsActive { updateRequired = true if record.Active { + if cfg.Verbose { + log.Printf("User %q is inactive, activating", record.Email) + } // Here we assume the 'setup' is done elsewhere if needed. err := UpdateUser(cfg.Client, user.UUID, &user, map[string]string{ "is_active": wantedActiveStatus, @@ -213,6 +264,9 @@ func ProcessRecord(cfg *ConfigParams, record userRecord, allUsers map[string]arv return false, fmt.Errorf("error updating user %q: %s", record.Email, err) } } else { + if cfg.Verbose { + log.Printf("User %q is active, deactivating", record.Email) + } err := UnsetupUser(cfg.Client, user.UUID, &user) if err != nil { return false, fmt.Errorf("error deactivating user %q: %s", record.Email, err) @@ -221,6 +275,9 @@ func ProcessRecord(cfg *ConfigParams, record userRecord, allUsers map[string]arv } // Inactive users cannot be admins. if user.IsActive && record.Admin != user.IsAdmin { + if cfg.Verbose { + log.Printf("User %q is active, changing admin status to %v", record.Email, record.Admin) + } updateRequired = true err := UpdateUser(cfg.Client, user.UUID, &user, map[string]string{ "is_admin": wantedAdminStatus, @@ -230,11 +287,14 @@ func ProcessRecord(cfg *ConfigParams, record userRecord, allUsers map[string]arv } } allUsers[record.Email] = user + if createRequired { + log.Printf("Created user %q", record.Email) + } if updateRequired { log.Printf("Updated user %q", record.Email) } - return updateRequired, nil + return createRequired || updateRequired, nil } // LoadInputFile reads the input file and returns a list of user records -- 2.30.2