github.com/wgh-/mattermost-server@v4.8.0-rc2+incompatible/cmd/platform/user.go (about)

     1  // Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
     2  // See License.txt for license information.
     3  package main
     4  
     5  import (
     6  	"encoding/json"
     7  	"errors"
     8  	"fmt"
     9  	"io/ioutil"
    10  
    11  	l4g "github.com/alecthomas/log4go"
    12  	"github.com/mattermost/mattermost-server/app"
    13  	"github.com/mattermost/mattermost-server/model"
    14  	"github.com/spf13/cobra"
    15  )
    16  
    17  var userCmd = &cobra.Command{
    18  	Use:   "user",
    19  	Short: "Management of users",
    20  }
    21  
    22  var userActivateCmd = &cobra.Command{
    23  	Use:   "activate [emails, usernames, userIds]",
    24  	Short: "Activate users",
    25  	Long:  "Activate users that have been deactivated.",
    26  	Example: `  user activate user@example.com
    27    user activate username`,
    28  	RunE: userActivateCmdF,
    29  }
    30  
    31  var userDeactivateCmd = &cobra.Command{
    32  	Use:   "deactivate [emails, usernames, userIds]",
    33  	Short: "Deactivate users",
    34  	Long:  "Deactivate users. Deactivated users are immediately logged out of all sessions and are unable to log back in.",
    35  	Example: `  user deactivate user@example.com
    36    user deactivate username`,
    37  	RunE: userDeactivateCmdF,
    38  }
    39  
    40  var userCreateCmd = &cobra.Command{
    41  	Use:     "create",
    42  	Short:   "Create a user",
    43  	Long:    "Create a user",
    44  	Example: `  user create --email user@example.com --username userexample --password Password1`,
    45  	RunE:    userCreateCmdF,
    46  }
    47  
    48  var userInviteCmd = &cobra.Command{
    49  	Use:   "invite [email] [teams]",
    50  	Short: "Send user an email invite to a team.",
    51  	Long: `Send user an email invite to a team.
    52  You can invite a user to multiple teams by listing them.
    53  You can specify teams by name or ID.`,
    54  	Example: `  user invite user@example.com myteam
    55    user invite user@example.com myteam1 myteam2`,
    56  	RunE: userInviteCmdF,
    57  }
    58  
    59  var resetUserPasswordCmd = &cobra.Command{
    60  	Use:     "password [user] [password]",
    61  	Short:   "Set a user's password",
    62  	Long:    "Set a user's password",
    63  	Example: "  user password user@example.com Password1",
    64  	RunE:    resetUserPasswordCmdF,
    65  }
    66  
    67  var resetUserMfaCmd = &cobra.Command{
    68  	Use:   "resetmfa [users]",
    69  	Short: "Turn off MFA",
    70  	Long: `Turn off multi-factor authentication for a user.
    71  If MFA enforcement is enabled, the user will be forced to re-enable MFA as soon as they login.`,
    72  	Example: "  user resetmfa user@example.com",
    73  	RunE:    resetUserMfaCmdF,
    74  }
    75  
    76  var deleteUserCmd = &cobra.Command{
    77  	Use:     "delete [users]",
    78  	Short:   "Delete users and all posts",
    79  	Long:    "Permanently delete user and all related information including posts.",
    80  	Example: "  user delete user@example.com",
    81  	RunE:    deleteUserCmdF,
    82  }
    83  
    84  var deleteAllUsersCmd = &cobra.Command{
    85  	Use:     "deleteall",
    86  	Short:   "Delete all users and all posts",
    87  	Long:    "Permanently delete all users and all related information including posts.",
    88  	Example: "  user deleteall",
    89  	RunE:    deleteAllUsersCommandF,
    90  }
    91  
    92  var migrateAuthCmd = &cobra.Command{
    93  	Use:     "migrate_auth [from_auth] [to_auth] [migration-options]",
    94  	Short:   "Mass migrate user accounts authentication type",
    95  	Long:    `Migrates accounts from one authentication provider to another. For example, you can upgrade your authentication provider from email to ldap.`,
    96  	Example: "  user migrate_auth email saml users.json",
    97  	Args: func(cmd *cobra.Command, args []string) error {
    98  		if len(args) < 2 {
    99  			return errors.New("Auth migration requires at least 2 arguments.")
   100  		}
   101  
   102  		toAuth := args[1]
   103  
   104  		if toAuth != "ldap" && toAuth != "saml" {
   105  			return errors.New("Invalid to_auth parameter, must be saml or ldap.")
   106  		}
   107  
   108  		if toAuth == "ldap" && len(args) != 3 {
   109  			return errors.New("Ldap migration requires 3 arguments.")
   110  		}
   111  
   112  		autoFlag, _ := cmd.Flags().GetBool("auto")
   113  
   114  		if toAuth == "saml" && autoFlag {
   115  			if len(args) != 2 {
   116  				return errors.New("Saml migration requires two arguments when using the --auto flag. See help text for details.")
   117  			}
   118  		}
   119  
   120  		if toAuth == "saml" && !autoFlag {
   121  			if len(args) != 3 {
   122  				return errors.New("Saml migration requires three arguments when not using the --auto flag. See help text for details.")
   123  			}
   124  		}
   125  		return nil
   126  	},
   127  	RunE: migrateAuthCmdF,
   128  }
   129  
   130  var verifyUserCmd = &cobra.Command{
   131  	Use:     "verify [users]",
   132  	Short:   "Verify email of users",
   133  	Long:    "Verify the emails of some users.",
   134  	Example: "  user verify user1",
   135  	RunE:    verifyUserCmdF,
   136  }
   137  
   138  var searchUserCmd = &cobra.Command{
   139  	Use:     "search [users]",
   140  	Short:   "Search for users",
   141  	Long:    "Search for users based on username, email, or user ID.",
   142  	Example: "  user search user1@mail.com user2@mail.com",
   143  	RunE:    searchUserCmdF,
   144  }
   145  
   146  func init() {
   147  	userCreateCmd.Flags().String("username", "", "Required. Username for the new user account.")
   148  	userCreateCmd.Flags().String("email", "", "Required. The email address for the new user account.")
   149  	userCreateCmd.Flags().String("password", "", "Required. The password for the new user account.")
   150  	userCreateCmd.Flags().String("nickname", "", "Optional. The nickname for the new user account.")
   151  	userCreateCmd.Flags().String("firstname", "", "Optional. The first name for the new user account.")
   152  	userCreateCmd.Flags().String("lastname", "", "Optional. The last name for the new user account.")
   153  	userCreateCmd.Flags().String("locale", "", "Optional. The locale (ex: en, fr) for the new user account.")
   154  	userCreateCmd.Flags().Bool("system_admin", false, "Optional. If supplied, the new user will be a system administrator. Defaults to false.")
   155  
   156  	deleteUserCmd.Flags().Bool("confirm", false, "Confirm you really want to delete the user and a DB backup has been performed.")
   157  
   158  	deleteAllUsersCmd.Flags().Bool("confirm", false, "Confirm you really want to delete the user and a DB backup has been performed.")
   159  
   160  	migrateAuthCmd.Flags().Bool("force", false, "Force the migration to occur even if there are duplicates on the LDAP server. Duplicates will not be migrated. (ldap only)")
   161  	migrateAuthCmd.Flags().Bool("auto", false, "Automatically migrate all users. Assumes the usernames and emails are identical between Mattermost and SAML services. (saml only)")
   162  	migrateAuthCmd.Flags().Bool("dryRun", false, "Run a simulation of the migration process without changing the database.")
   163  	migrateAuthCmd.SetUsageTemplate(`Usage:
   164    platform user migrate_auth [from_auth] [to_auth] [migration-options] [flags]
   165  
   166  Examples:
   167  {{.Example}}
   168  
   169  Arguments:
   170    from_auth:
   171      The authentication service to migrate users accounts from.
   172      Supported options: email, gitlab, ldap, saml.
   173  
   174    to_auth:
   175      The authentication service to migrate users to.
   176      Supported options: ldap, saml.
   177  
   178    migration-options:
   179      Migration specific options, full command help for more information.
   180  
   181  Flags:
   182  {{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}
   183  
   184  Global Flags:
   185  {{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}
   186  `)
   187  	migrateAuthCmd.SetHelpTemplate(`Usage:
   188    platform user migrate_auth [from_auth] [to_auth] [migration-options] [flags]
   189  
   190  Examples:
   191  {{.Example}}
   192  
   193  Arguments:
   194    from_auth:
   195      The authentication service to migrate users accounts from.
   196      Supported options: email, gitlab, ldap, saml.
   197  
   198    to_auth:
   199      The authentication service to migrate users to.
   200      Supported options: ldap, saml.
   201  
   202    migration-options (ldap):
   203      match_field:
   204        The field that is guaranteed to be the same in both authentication services. For example, if the users emails are consistent set to email.
   205        Supported options: email, username.
   206  
   207    migration-options (saml):
   208      users_file:
   209        The path of a json file with the usernames and emails of all users to migrate to SAML. The username and email must be the same that the SAML service provider store. And the email must match with the email in mattermost database.
   210  
   211        Example json content:
   212          {
   213            "usr1@email.com": "usr.one",
   214            "usr2@email.com": "usr.two"
   215          }
   216  
   217  Flags:
   218  {{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}
   219  
   220  Global Flags:
   221  {{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}
   222  `)
   223  
   224  	userCmd.AddCommand(
   225  		userActivateCmd,
   226  		userDeactivateCmd,
   227  		userCreateCmd,
   228  		userInviteCmd,
   229  		resetUserPasswordCmd,
   230  		resetUserMfaCmd,
   231  		deleteUserCmd,
   232  		deleteAllUsersCmd,
   233  		migrateAuthCmd,
   234  		verifyUserCmd,
   235  		searchUserCmd,
   236  	)
   237  }
   238  
   239  func userActivateCmdF(cmd *cobra.Command, args []string) error {
   240  	a, err := initDBCommandContextCobra(cmd)
   241  	if err != nil {
   242  		return err
   243  	}
   244  
   245  	if len(args) < 1 {
   246  		return errors.New("Expected at least one argument. See help text for details.")
   247  	}
   248  
   249  	changeUsersActiveStatus(a, args, true)
   250  	return nil
   251  }
   252  
   253  func changeUsersActiveStatus(a *app.App, userArgs []string, active bool) {
   254  	users := getUsersFromUserArgs(a, userArgs)
   255  	for i, user := range users {
   256  		err := changeUserActiveStatus(a, user, userArgs[i], active)
   257  
   258  		if err != nil {
   259  			CommandPrintErrorln(err.Error())
   260  		}
   261  	}
   262  }
   263  
   264  func changeUserActiveStatus(a *app.App, user *model.User, userArg string, activate bool) error {
   265  	if user == nil {
   266  		return fmt.Errorf("Can't find user '%v'", userArg)
   267  	}
   268  	if user.IsSSOUser() {
   269  		fmt.Println("You must also deactivate this user in the SSO provider or they will be reactivated on next login or sync.")
   270  	}
   271  	if _, err := a.UpdateActive(user, activate); err != nil {
   272  		return fmt.Errorf("Unable to change activation status of user: %v", userArg)
   273  	}
   274  
   275  	return nil
   276  }
   277  
   278  func userDeactivateCmdF(cmd *cobra.Command, args []string) error {
   279  	a, err := initDBCommandContextCobra(cmd)
   280  	if err != nil {
   281  		return err
   282  	}
   283  
   284  	if len(args) < 1 {
   285  		return errors.New("Expected at least one argument. See help text for details.")
   286  	}
   287  
   288  	changeUsersActiveStatus(a, args, false)
   289  	return nil
   290  }
   291  
   292  func userCreateCmdF(cmd *cobra.Command, args []string) error {
   293  	a, err := initDBCommandContextCobra(cmd)
   294  	if err != nil {
   295  		return err
   296  	}
   297  
   298  	username, erru := cmd.Flags().GetString("username")
   299  	if erru != nil || username == "" {
   300  		return errors.New("Username is required")
   301  	}
   302  	email, erre := cmd.Flags().GetString("email")
   303  	if erre != nil || email == "" {
   304  		return errors.New("Email is required")
   305  	}
   306  	password, errp := cmd.Flags().GetString("password")
   307  	if errp != nil || password == "" {
   308  		return errors.New("Password is required")
   309  	}
   310  	nickname, _ := cmd.Flags().GetString("nickname")
   311  	firstname, _ := cmd.Flags().GetString("firstname")
   312  	lastname, _ := cmd.Flags().GetString("lastname")
   313  	locale, _ := cmd.Flags().GetString("locale")
   314  	systemAdmin, _ := cmd.Flags().GetBool("system_admin")
   315  
   316  	user := &model.User{
   317  		Username:  username,
   318  		Email:     email,
   319  		Password:  password,
   320  		Nickname:  nickname,
   321  		FirstName: firstname,
   322  		LastName:  lastname,
   323  		Locale:    locale,
   324  	}
   325  
   326  	if ruser, err := a.CreateUser(user); err != nil {
   327  		return errors.New("Unable to create user. Error: " + err.Error())
   328  	} else if systemAdmin {
   329  		a.UpdateUserRoles(ruser.Id, "system_user system_admin", false)
   330  	}
   331  
   332  	CommandPrettyPrintln("Created User")
   333  
   334  	return nil
   335  }
   336  
   337  func userInviteCmdF(cmd *cobra.Command, args []string) error {
   338  	a, err := initDBCommandContextCobra(cmd)
   339  	if err != nil {
   340  		return err
   341  	}
   342  
   343  	if len(args) < 2 {
   344  		return errors.New("Expected at least two arguments. See help text for details.")
   345  	}
   346  
   347  	email := args[0]
   348  	if !model.IsValidEmail(email) {
   349  		return errors.New("Invalid email")
   350  	}
   351  
   352  	teams := getTeamsFromTeamArgs(a, args[1:])
   353  	for i, team := range teams {
   354  		err := inviteUser(a, email, team, args[i+1])
   355  
   356  		if err != nil {
   357  			CommandPrintErrorln(err.Error())
   358  		}
   359  	}
   360  
   361  	return nil
   362  }
   363  
   364  func inviteUser(a *app.App, email string, team *model.Team, teamArg string) error {
   365  	invites := []string{email}
   366  	if team == nil {
   367  		return fmt.Errorf("Can't find team '%v'", teamArg)
   368  	}
   369  
   370  	a.SendInviteEmails(team, "Administrator", invites, *a.Config().ServiceSettings.SiteURL)
   371  	CommandPrettyPrintln("Invites may or may not have been sent.")
   372  
   373  	return nil
   374  }
   375  
   376  func resetUserPasswordCmdF(cmd *cobra.Command, args []string) error {
   377  	a, err := initDBCommandContextCobra(cmd)
   378  	if err != nil {
   379  		return err
   380  	}
   381  
   382  	if len(args) != 2 {
   383  		return errors.New("Expected two arguments. See help text for details.")
   384  	}
   385  
   386  	user := getUserFromUserArg(a, args[0])
   387  	if user == nil {
   388  		return errors.New("Unable to find user '" + args[0] + "'")
   389  	}
   390  	password := args[1]
   391  
   392  	if result := <-a.Srv.Store.User().UpdatePassword(user.Id, model.HashPassword(password)); result.Err != nil {
   393  		return result.Err
   394  	}
   395  
   396  	return nil
   397  }
   398  
   399  func resetUserMfaCmdF(cmd *cobra.Command, args []string) error {
   400  	a, err := initDBCommandContextCobra(cmd)
   401  	if err != nil {
   402  		return err
   403  	}
   404  
   405  	if len(args) < 1 {
   406  		return errors.New("Expected at least one argument. See help text for details.")
   407  	}
   408  
   409  	users := getUsersFromUserArgs(a, args)
   410  
   411  	for i, user := range users {
   412  		if user == nil {
   413  			return errors.New("Unable to find user '" + args[i] + "'")
   414  		}
   415  
   416  		if err := a.DeactivateMfa(user.Id); err != nil {
   417  			return err
   418  		}
   419  	}
   420  
   421  	return nil
   422  }
   423  
   424  func deleteUserCmdF(cmd *cobra.Command, args []string) error {
   425  	a, err := initDBCommandContextCobra(cmd)
   426  	if err != nil {
   427  		return err
   428  	}
   429  
   430  	if len(args) < 1 {
   431  		return errors.New("Expected at least one argument. See help text for details.")
   432  	}
   433  
   434  	confirmFlag, _ := cmd.Flags().GetBool("confirm")
   435  	if !confirmFlag {
   436  		var confirm string
   437  		CommandPrettyPrintln("Have you performed a database backup? (YES/NO): ")
   438  		fmt.Scanln(&confirm)
   439  
   440  		if confirm != "YES" {
   441  			return errors.New("ABORTED: You did not answer YES exactly, in all capitals.")
   442  		}
   443  		CommandPrettyPrintln("Are you sure you want to permanently delete the specified users? (YES/NO): ")
   444  		fmt.Scanln(&confirm)
   445  		if confirm != "YES" {
   446  			return errors.New("ABORTED: You did not answer YES exactly, in all capitals.")
   447  		}
   448  	}
   449  
   450  	users := getUsersFromUserArgs(a, args)
   451  
   452  	for i, user := range users {
   453  		if user == nil {
   454  			return errors.New("Unable to find user '" + args[i] + "'")
   455  		}
   456  
   457  		if err := a.PermanentDeleteUser(user); err != nil {
   458  			return err
   459  		}
   460  	}
   461  
   462  	return nil
   463  }
   464  
   465  func deleteAllUsersCommandF(cmd *cobra.Command, args []string) error {
   466  	a, err := initDBCommandContextCobra(cmd)
   467  	if err != nil {
   468  		return err
   469  	}
   470  
   471  	if len(args) > 0 {
   472  		return errors.New("Expected zero arguments.")
   473  	}
   474  
   475  	confirmFlag, _ := cmd.Flags().GetBool("confirm")
   476  	if !confirmFlag {
   477  		var confirm string
   478  		CommandPrettyPrintln("Have you performed a database backup? (YES/NO): ")
   479  		fmt.Scanln(&confirm)
   480  
   481  		if confirm != "YES" {
   482  			return errors.New("ABORTED: You did not answer YES exactly, in all capitals.")
   483  		}
   484  		CommandPrettyPrintln("Are you sure you want to permanently delete all user accounts? (YES/NO): ")
   485  		fmt.Scanln(&confirm)
   486  		if confirm != "YES" {
   487  			return errors.New("ABORTED: You did not answer YES exactly, in all capitals.")
   488  		}
   489  	}
   490  
   491  	if err := a.PermanentDeleteAllUsers(); err != nil {
   492  		return err
   493  	}
   494  
   495  	CommandPrettyPrintln("All user accounts successfully deleted.")
   496  	return nil
   497  }
   498  
   499  func migrateAuthCmdF(cmd *cobra.Command, args []string) error {
   500  	if args[1] == "saml" {
   501  		return migrateAuthToSamlCmdF(cmd, args)
   502  	}
   503  	return migrateAuthToLdapCmdF(cmd, args)
   504  }
   505  
   506  func migrateAuthToLdapCmdF(cmd *cobra.Command, args []string) error {
   507  	a, err := initDBCommandContextCobra(cmd)
   508  	if err != nil {
   509  		return err
   510  	}
   511  
   512  	fromAuth := args[0]
   513  	matchField := args[1]
   514  
   515  	if len(fromAuth) == 0 || (fromAuth != "email" && fromAuth != "gitlab" && fromAuth != "saml") {
   516  		return errors.New("Invalid from_auth argument")
   517  	}
   518  
   519  	// Email auth in Mattermost system is represented by ""
   520  	if fromAuth == "email" {
   521  		fromAuth = ""
   522  	}
   523  
   524  	if len(matchField) == 0 || (matchField != "email" && matchField != "username") {
   525  		return errors.New("Invalid match_field argument")
   526  	}
   527  
   528  	forceFlag, _ := cmd.Flags().GetBool("force")
   529  	dryRunFlag, _ := cmd.Flags().GetBool("dryRun")
   530  
   531  	if migrate := a.AccountMigration; migrate != nil {
   532  		if err := migrate.MigrateToLdap(fromAuth, matchField, forceFlag, dryRunFlag); err != nil {
   533  			return errors.New("Error while migrating users: " + err.Error())
   534  		}
   535  
   536  		CommandPrettyPrintln("Sucessfully migrated accounts.")
   537  	}
   538  
   539  	return nil
   540  }
   541  
   542  func migrateAuthToSamlCmdF(cmd *cobra.Command, args []string) error {
   543  	a, err := initDBCommandContextCobra(cmd)
   544  	if err != nil {
   545  		return err
   546  	}
   547  
   548  	dryRunFlag, _ := cmd.Flags().GetBool("dryRun")
   549  	autoFlag, _ := cmd.Flags().GetBool("auto")
   550  
   551  	matchesFile := ""
   552  	matches := map[string]string{}
   553  	if !autoFlag {
   554  		matchesFile = args[1]
   555  
   556  		file, e := ioutil.ReadFile(matchesFile)
   557  		if e != nil {
   558  			return errors.New("Invalid users file.")
   559  		}
   560  		if json.Unmarshal(file, &matches) != nil {
   561  			return errors.New("Invalid users file.")
   562  		}
   563  	}
   564  
   565  	fromAuth := args[0]
   566  
   567  	if len(fromAuth) == 0 || (fromAuth != "email" && fromAuth != "gitlab" && fromAuth != "ldap") {
   568  		return errors.New("Invalid from_auth argument")
   569  	}
   570  
   571  	if autoFlag && !dryRunFlag {
   572  		var confirm string
   573  		CommandPrettyPrintln("You are about to perform an automatic \"" + fromAuth + " to saml\" migration. This must only be done if your current Mattermost users with " + fromAuth + " auth have the same username and email in your SAML service. Otherwise, provide the usernames and emails from your SAML Service using the \"users file\" without the \"--auto\" option.\n\nDo you want to proceed with automatic migration anyway? (YES/NO):")
   574  		fmt.Scanln(&confirm)
   575  
   576  		if confirm != "YES" {
   577  			return errors.New("ABORTED: You did not answer YES exactly, in all capitals.")
   578  		}
   579  	}
   580  
   581  	// Email auth in Mattermost system is represented by ""
   582  	if fromAuth == "email" {
   583  		fromAuth = ""
   584  	}
   585  
   586  	if migrate := a.AccountMigration; migrate != nil {
   587  		if err := migrate.MigrateToSaml(fromAuth, matches, autoFlag, dryRunFlag); err != nil {
   588  			return errors.New("Error while migrating users: " + err.Error())
   589  		}
   590  		l4g.Close()
   591  		CommandPrettyPrintln("Sucessfully migrated accounts.")
   592  	}
   593  
   594  	return nil
   595  }
   596  
   597  func verifyUserCmdF(cmd *cobra.Command, args []string) error {
   598  	a, err := initDBCommandContextCobra(cmd)
   599  	if err != nil {
   600  		return err
   601  	}
   602  
   603  	if len(args) < 1 {
   604  		return errors.New("Expected at least one argument. See help text for details.")
   605  	}
   606  
   607  	users := getUsersFromUserArgs(a, args)
   608  
   609  	for i, user := range users {
   610  		if user == nil {
   611  			CommandPrintErrorln("Unable to find user '" + args[i] + "'")
   612  			continue
   613  		}
   614  		if cresult := <-a.Srv.Store.User().VerifyEmail(user.Id); cresult.Err != nil {
   615  			CommandPrintErrorln("Unable to verify '" + args[i] + "' email. Error: " + cresult.Err.Error())
   616  		}
   617  	}
   618  
   619  	return nil
   620  }
   621  
   622  func searchUserCmdF(cmd *cobra.Command, args []string) error {
   623  	a, err := initDBCommandContextCobra(cmd)
   624  	if err != nil {
   625  		return err
   626  	}
   627  
   628  	if len(args) < 1 {
   629  		return errors.New("Expected at least one argument. See help text for details.")
   630  	}
   631  
   632  	users := getUsersFromUserArgs(a, args)
   633  
   634  	for i, user := range users {
   635  		if i > 0 {
   636  			CommandPrettyPrintln("------------------------------")
   637  		}
   638  		if user == nil {
   639  			CommandPrintErrorln("Unable to find user '" + args[i] + "'")
   640  			continue
   641  		}
   642  
   643  		CommandPrettyPrintln("id: " + user.Id)
   644  		CommandPrettyPrintln("username: " + user.Username)
   645  		CommandPrettyPrintln("nickname: " + user.Nickname)
   646  		CommandPrettyPrintln("position: " + user.Position)
   647  		CommandPrettyPrintln("first_name: " + user.FirstName)
   648  		CommandPrettyPrintln("last_name: " + user.LastName)
   649  		CommandPrettyPrintln("email: " + user.Email)
   650  		CommandPrettyPrintln("auth_service: " + user.AuthService)
   651  	}
   652  
   653  	return nil
   654  }