github.com/masterhung0112/hk_server/v5@v5.0.0-20220302090640-ec71aef15e1c/cmd/hkserver/commands/user.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See LICENSE.txt for license information.
     3  
     4  package commands
     5  
     6  import (
     7  	"encoding/json"
     8  	"errors"
     9  	"fmt"
    10  	"io/ioutil"
    11  	"strings"
    12  
    13  	"github.com/spf13/cobra"
    14  
    15  	"github.com/masterhung0112/hk_server/v5/app"
    16  	"github.com/masterhung0112/hk_server/v5/app/request"
    17  	"github.com/masterhung0112/hk_server/v5/audit"
    18  	"github.com/masterhung0112/hk_server/v5/model"
    19  	"github.com/masterhung0112/hk_server/v5/services/users"
    20  )
    21  
    22  var UserCmd = &cobra.Command{
    23  	Use:   "user",
    24  	Short: "Management of users",
    25  }
    26  
    27  var UserActivateCmd = &cobra.Command{
    28  	Use:   "activate [emails, usernames, userIds]",
    29  	Short: "Activate users",
    30  	Long:  "Activate users that have been deactivated.",
    31  	Example: `  user activate user@example.com
    32    user activate username`,
    33  	RunE: userActivateCmdF,
    34  }
    35  
    36  var UserDeactivateCmd = &cobra.Command{
    37  	Use:   "deactivate [emails, usernames, userIds]",
    38  	Short: "Deactivate users",
    39  	Long:  "Deactivate users. Deactivated users are immediately logged out of all sessions and are unable to log back in.",
    40  	Example: `  user deactivate user@example.com
    41    user deactivate username`,
    42  	RunE: userDeactivateCmdF,
    43  }
    44  
    45  var UserCreateCmd = &cobra.Command{
    46  	Use:     "create",
    47  	Short:   "Create a user",
    48  	Long:    "Create a user",
    49  	Example: `  user create --email user@example.com --username userexample --password Password1`,
    50  	RunE:    userCreateCmdF,
    51  }
    52  
    53  var UserConvertCmd = &cobra.Command{
    54  	Use:   "convert [emails, usernames, userIds] --bot",
    55  	Short: "Convert users to bots, or a bot to a user",
    56  	Long:  "Convert users to bots, or a bot to a user",
    57  	Example: `  user convert user@example.com anotherUser --bot
    58  	user convert botusername --email new.email@email.com --password password --user`,
    59  	Args: cobra.MinimumNArgs(1),
    60  	RunE: userConvertCmdF,
    61  }
    62  
    63  var UserInviteCmd = &cobra.Command{
    64  	Use:   "invite [email] [teams]",
    65  	Short: "Send user an email invite to a team.",
    66  	Long: `Send user an email invite to a team.
    67  You can invite a user to multiple teams by listing them.
    68  You can specify teams by name or ID.`,
    69  	Example: `  user invite user@example.com myteam
    70    user invite user@example.com myteam1 myteam2`,
    71  	RunE: userInviteCmdF,
    72  }
    73  
    74  var ResetUserPasswordCmd = &cobra.Command{
    75  	Use:     "password [user] [password]",
    76  	Short:   "Set a user's password",
    77  	Long:    "Set a user's password",
    78  	Example: "  user password user@example.com Password1",
    79  	RunE:    resetUserPasswordCmdF,
    80  }
    81  
    82  var updateUserEmailCmd = &cobra.Command{
    83  	Use:     "email [user] [new email]",
    84  	Short:   "Change email of the user",
    85  	Long:    "Change email of the user.",
    86  	Example: "  user email testuser user@example.com",
    87  	RunE:    updateUserEmailCmdF,
    88  }
    89  
    90  var ResetUserMfaCmd = &cobra.Command{
    91  	Use:   "resetmfa [users]",
    92  	Short: "Turn off MFA",
    93  	Long: `Turn off multi-factor authentication for a user.
    94  If MFA enforcement is enabled, the user will be forced to re-enable MFA as soon as they login.`,
    95  	Example: "  user resetmfa user@example.com",
    96  	RunE:    resetUserMfaCmdF,
    97  }
    98  
    99  var DeleteUserCmd = &cobra.Command{
   100  	Use:     "delete [users]",
   101  	Short:   "Delete users and all posts",
   102  	Long:    "Permanently delete user and all related information including posts.",
   103  	Example: "  user delete user@example.com",
   104  	RunE:    deleteUserCmdF,
   105  }
   106  
   107  var DeleteAllUsersCmd = &cobra.Command{
   108  	Use:     "deleteall",
   109  	Short:   "Delete all users and all posts",
   110  	Long:    "Permanently delete all users and all related information including posts.",
   111  	Example: "  user deleteall",
   112  	RunE:    deleteAllUsersCommandF,
   113  }
   114  
   115  var MigrateAuthCmd = &cobra.Command{
   116  	Use:     "migrate_auth [from_auth] [to_auth] [migration-options]",
   117  	Short:   "Mass migrate user accounts authentication type",
   118  	Long:    `Migrates accounts from one authentication provider to another. For example, you can upgrade your authentication provider from email to ldap.`,
   119  	Example: "  user migrate_auth email saml users.json",
   120  	Args: func(command *cobra.Command, args []string) error {
   121  		if len(args) < 2 {
   122  			return errors.New("Auth migration requires at least 2 arguments.")
   123  		}
   124  
   125  		toAuth := args[1]
   126  
   127  		if toAuth != "ldap" && toAuth != "saml" {
   128  			return errors.New("Invalid to_auth parameter, must be saml or ldap.")
   129  		}
   130  
   131  		if toAuth == "ldap" && len(args) != 3 {
   132  			return errors.New("Ldap migration requires 3 arguments.")
   133  		}
   134  
   135  		autoFlag, _ := command.Flags().GetBool("auto")
   136  
   137  		if toAuth == "saml" && autoFlag {
   138  			if len(args) != 2 {
   139  				return errors.New("Saml migration requires two arguments when using the --auto flag. See help text for details.")
   140  			}
   141  		}
   142  
   143  		if toAuth == "saml" && !autoFlag {
   144  			if len(args) != 3 {
   145  				return errors.New("Saml migration requires three arguments when not using the --auto flag. See help text for details.")
   146  			}
   147  		}
   148  		return nil
   149  	},
   150  	RunE: migrateAuthCmdF,
   151  }
   152  
   153  var VerifyUserCmd = &cobra.Command{
   154  	Use:     "verify [users]",
   155  	Short:   "Verify email of users",
   156  	Long:    "Verify the emails of some users.",
   157  	Example: "  user verify user1",
   158  	RunE:    verifyUserCmdF,
   159  }
   160  
   161  var SearchUserCmd = &cobra.Command{
   162  	Use:     "search [users]",
   163  	Short:   "Search for users",
   164  	Long:    "Search for users based on username, email, or user ID.",
   165  	Example: "  user search user1@mail.com user2@mail.com",
   166  	RunE:    searchUserCmdF,
   167  }
   168  
   169  func init() {
   170  	UserCreateCmd.Flags().String("username", "", "Required. Username for the new user account.")
   171  	UserCreateCmd.Flags().String("email", "", "Required. The email address for the new user account.")
   172  	UserCreateCmd.Flags().String("password", "", "Required. The password for the new user account.")
   173  	UserCreateCmd.Flags().String("nickname", "", "Optional. The nickname for the new user account.")
   174  	UserCreateCmd.Flags().String("firstname", "", "Optional. The first name for the new user account.")
   175  	UserCreateCmd.Flags().String("lastname", "", "Optional. The last name for the new user account.")
   176  	UserCreateCmd.Flags().String("locale", "", "Optional. The locale (ex: en, fr) for the new user account.")
   177  	UserCreateCmd.Flags().Bool("system_admin", false, "Optional. If supplied, the new user will be a system administrator. Defaults to false.")
   178  
   179  	UserConvertCmd.Flags().Bool("bot", false, "If supplied, convert users to bots.")
   180  	UserConvertCmd.Flags().Bool("user", false, "If supplied, convert a bot to a user.")
   181  	UserConvertCmd.Flags().String("password", "", "The password for converted new user account. Required when \"user\" flag is set.")
   182  	UserConvertCmd.Flags().String("username", "", "Username for the converted user account. Ignored when \"user\" flag is missing.")
   183  	UserConvertCmd.Flags().String("email", "", "The email address for the converted user account. Ignored when \"user\" flag is missing.")
   184  	UserConvertCmd.Flags().String("nickname", "", "The nickname for the converted user account. Ignored when \"user\" flag is missing.")
   185  	UserConvertCmd.Flags().String("firstname", "", "The first name for the converted user account. Ignored when \"user\" flag is missing.")
   186  	UserConvertCmd.Flags().String("lastname", "", "The last name for the converted user account. Ignored when \"user\" flag is missing.")
   187  	UserConvertCmd.Flags().String("locale", "", "The locale (ex: en, fr) for converted new user account. Ignored when \"user\" flag is missing.")
   188  	UserConvertCmd.Flags().Bool("system_admin", false, "If supplied, the converted user will be a system administrator. Defaults to false. Ignored when \"user\" flag is missing.")
   189  
   190  	DeleteUserCmd.Flags().Bool("confirm", false, "Confirm you really want to delete the user and a DB backup has been performed.")
   191  
   192  	DeleteAllUsersCmd.Flags().Bool("confirm", false, "Confirm you really want to delete the user and a DB backup has been performed.")
   193  
   194  	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)")
   195  	MigrateAuthCmd.Flags().Bool("auto", false, "Automatically migrate all users. Assumes the usernames and emails are identical between Mattermost and SAML services. (saml only)")
   196  	MigrateAuthCmd.Flags().Bool("dryRun", false, "Run a simulation of the migration process without changing the database.")
   197  	MigrateAuthCmd.SetUsageTemplate(`Usage:
   198    mattermost user migrate_auth [from_auth] [to_auth] [migration-options] [flags]
   199  
   200  Examples:
   201  {{.Example}}
   202  
   203  Arguments:
   204    from_auth:
   205      The authentication service to migrate users accounts from.
   206      Supported options: email, gitlab, ldap, saml.
   207  
   208    to_auth:
   209      The authentication service to migrate users to.
   210      Supported options: ldap, saml.
   211  
   212    migration-options:
   213      Migration specific options, full command help for more information.
   214  
   215  Flags:
   216  {{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}
   217  
   218  Global Flags:
   219  {{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}
   220  `)
   221  	MigrateAuthCmd.SetHelpTemplate(`Usage:
   222    mattermost user migrate_auth [from_auth] [to_auth] [migration-options] [flags]
   223  
   224  Examples:
   225  {{.Example}}
   226  
   227  Arguments:
   228    from_auth:
   229      The authentication service to migrate users accounts from.
   230      Supported options: email, gitlab, ldap, saml.
   231  
   232    to_auth:
   233      The authentication service to migrate users to.
   234      Supported options: ldap, saml.
   235  
   236    migration-options (ldap):
   237      match_field:
   238        The field that is guaranteed to be the same in both authentication services. For example, if the users emails are consistent set to email.
   239        Supported options: email, username.
   240  
   241    migration-options (saml):
   242      users_file:
   243        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.
   244  
   245        Example json content:
   246          {
   247            "usr1@email.com": "usr.one",
   248            "usr2@email.com": "usr.two"
   249          }
   250  
   251  Flags:
   252  {{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}
   253  
   254  Global Flags:
   255  {{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}
   256  `)
   257  
   258  	UserCmd.AddCommand(
   259  		UserActivateCmd,
   260  		UserDeactivateCmd,
   261  		UserCreateCmd,
   262  		UserConvertCmd,
   263  		UserInviteCmd,
   264  		ResetUserPasswordCmd,
   265  		updateUserEmailCmd,
   266  		ResetUserMfaCmd,
   267  		DeleteUserCmd,
   268  		DeleteAllUsersCmd,
   269  		MigrateAuthCmd,
   270  		VerifyUserCmd,
   271  		SearchUserCmd,
   272  	)
   273  	RootCmd.AddCommand(UserCmd)
   274  }
   275  
   276  func userActivateCmdF(command *cobra.Command, args []string) error {
   277  	a, err := InitDBCommandContextCobra(command)
   278  	if err != nil {
   279  		return err
   280  	}
   281  	defer a.Srv().Shutdown()
   282  
   283  	if len(args) < 1 {
   284  		return errors.New("Expected at least one argument. See help text for details.")
   285  	}
   286  
   287  	changeUsersActiveStatus(a, args, true)
   288  
   289  	return nil
   290  }
   291  
   292  func changeUsersActiveStatus(a *app.App, userArgs []string, active bool) {
   293  	users := getUsersFromUserArgs(a, userArgs)
   294  	for i, user := range users {
   295  		err := changeUserActiveStatus(a, user, userArgs[i], active)
   296  
   297  		if err != nil {
   298  			CommandPrintErrorln(err.Error())
   299  		}
   300  	}
   301  }
   302  
   303  func changeUserActiveStatus(a *app.App, user *model.User, userArg string, activate bool) error {
   304  	if user == nil {
   305  		return fmt.Errorf("Can't find user '%v'", userArg)
   306  	}
   307  	if user.IsSSOUser() {
   308  		fmt.Println("You must also deactivate this user in the SSO provider or they will be reactivated on next login or sync.")
   309  	}
   310  	updatedUser, err := a.UpdateActive(&request.Context{}, user, activate)
   311  	if err != nil {
   312  		return fmt.Errorf("Unable to change activation status of user: %v", userArg)
   313  	}
   314  
   315  	auditRec := a.MakeAuditRecord("changeActiveUserStatus", audit.Success)
   316  	auditRec.AddMeta("user", updatedUser)
   317  	auditRec.AddMeta("activate", activate)
   318  	a.LogAuditRec(auditRec, nil)
   319  
   320  	return nil
   321  }
   322  
   323  func userDeactivateCmdF(command *cobra.Command, args []string) error {
   324  	a, err := InitDBCommandContextCobra(command)
   325  	if err != nil {
   326  		return err
   327  	}
   328  	defer a.Srv().Shutdown()
   329  
   330  	if len(args) < 1 {
   331  		return errors.New("Expected at least one argument. See help text for details.")
   332  	}
   333  
   334  	changeUsersActiveStatus(a, args, false)
   335  
   336  	return nil
   337  }
   338  
   339  //nolint:unparam
   340  func userCreateCmdF(command *cobra.Command, args []string) error {
   341  	a, err := InitDBCommandContextCobra(command)
   342  	if err != nil {
   343  		return err
   344  	}
   345  	defer a.Srv().Shutdown()
   346  
   347  	username, erru := command.Flags().GetString("username")
   348  	if erru != nil || username == "" {
   349  		return errors.New("Username is required")
   350  	}
   351  	email, erre := command.Flags().GetString("email")
   352  	if erre != nil || email == "" {
   353  		return errors.New("Email is required")
   354  	}
   355  	email = strings.ToLower((email))
   356  	password, errp := command.Flags().GetString("password")
   357  	if errp != nil || password == "" {
   358  		return errors.New("Password is required")
   359  	}
   360  	nickname, _ := command.Flags().GetString("nickname")
   361  	firstname, _ := command.Flags().GetString("firstname")
   362  	lastname, _ := command.Flags().GetString("lastname")
   363  	locale, _ := command.Flags().GetString("locale")
   364  	systemAdmin, _ := command.Flags().GetBool("system_admin")
   365  
   366  	user := &model.User{
   367  		Username:  username,
   368  		Email:     email,
   369  		Password:  password,
   370  		Nickname:  nickname,
   371  		FirstName: firstname,
   372  		LastName:  lastname,
   373  		Locale:    locale,
   374  	}
   375  
   376  	ruser, err := a.CreateUser(&request.Context{}, user)
   377  	if ruser == nil {
   378  		return errors.New("Unable to create user. Error: " + err.Error())
   379  	}
   380  
   381  	if systemAdmin {
   382  		if _, err := a.UpdateUserRolesWithUser(ruser, "system_user system_admin", false); err != nil {
   383  			return errors.New("Unable to make user system admin. Error: " + err.Error())
   384  		}
   385  	} else {
   386  		// This else case exists to prevent the first user created from being
   387  		// created as a system admin unless explicitly specified.
   388  		if _, err := a.UpdateUserRolesWithUser(ruser, "system_user", false); err != nil {
   389  			return errors.New("If this is the first user: Unable to prevent user from being system admin. Error: " + err.Error())
   390  		}
   391  	}
   392  
   393  	CommandPrettyPrintln("id: " + ruser.Id)
   394  	CommandPrettyPrintln("username: " + ruser.Username)
   395  	CommandPrettyPrintln("nickname: " + ruser.Nickname)
   396  	CommandPrettyPrintln("position: " + ruser.Position)
   397  	CommandPrettyPrintln("first_name: " + ruser.FirstName)
   398  	CommandPrettyPrintln("last_name: " + ruser.LastName)
   399  	CommandPrettyPrintln("email: " + ruser.Email)
   400  	CommandPrettyPrintln("auth_service: " + ruser.AuthService)
   401  
   402  	auditRec := a.MakeAuditRecord("userCreate", audit.Success)
   403  	auditRec.AddMeta("user", ruser)
   404  	auditRec.AddMeta("system_admin", systemAdmin)
   405  	a.LogAuditRec(auditRec, nil)
   406  
   407  	return nil
   408  }
   409  
   410  func usersToBots(args []string, a *app.App) {
   411  	users := getUsersFromUserArgs(a, args)
   412  	for i, user := range users {
   413  		if user == nil {
   414  			CommandPrintErrorln(fmt.Errorf("Unable to find user \"%s\"", args[i]))
   415  			continue
   416  		}
   417  
   418  		bot, err := a.ConvertUserToBot(user)
   419  		if err != nil {
   420  			CommandPrintErrorln(err.Error())
   421  			continue
   422  		}
   423  
   424  		CommandPrettyPrintln(fmt.Sprintf("User %s is converted to bot successfully", bot.UserId))
   425  
   426  		auditRec := a.MakeAuditRecord("userToBot", audit.Success)
   427  		auditRec.AddMeta("user", user)
   428  		a.LogAuditRec(auditRec, nil)
   429  	}
   430  }
   431  
   432  func getUpdatedPassword(command *cobra.Command) (string, error) {
   433  	password, err := command.Flags().GetString("password")
   434  	if err != nil {
   435  		return "", fmt.Errorf("Unable to read password. Error: %s", err.Error())
   436  	}
   437  
   438  	if password == "" {
   439  		return "", errors.New("Password is required.")
   440  	}
   441  
   442  	return password, nil
   443  }
   444  
   445  func getUpdatedUserModel(command *cobra.Command, a *app.App, user *model.User) (*model.User, error) {
   446  	username, _ := command.Flags().GetString("username")
   447  	if username == "" {
   448  		if user.Username == "" {
   449  			return nil, errors.New("Invalid username. Username is empty.")
   450  		}
   451  	} else {
   452  		user.Username = username
   453  	}
   454  
   455  	email, _ := command.Flags().GetString("email")
   456  	if email == "" {
   457  		if user.Email == "" {
   458  			return nil, errors.New("Invalid email. Email is empty.")
   459  		}
   460  	} else {
   461  		user.Email = email
   462  	}
   463  
   464  	nickname, _ := command.Flags().GetString("nickname")
   465  	if nickname != "" {
   466  		user.Nickname = nickname
   467  	}
   468  
   469  	firstname, _ := command.Flags().GetString("firstname")
   470  	if firstname != "" {
   471  		user.FirstName = firstname
   472  	}
   473  
   474  	lastname, _ := command.Flags().GetString("lastname")
   475  	if lastname != "" {
   476  		user.LastName = lastname
   477  	}
   478  
   479  	locale, _ := command.Flags().GetString("locale")
   480  	if locale != "" {
   481  		user.Locale = locale
   482  	}
   483  
   484  	if !user.IsLDAPUser() && !user.IsSAMLUser() && !users.CheckUserDomain(user, *a.Config().TeamSettings.RestrictCreationToDomains) {
   485  		return nil, errors.New("The email does not belong to an accepted domain.")
   486  	}
   487  
   488  	return user, nil
   489  }
   490  
   491  func botToUser(command *cobra.Command, args []string, a *app.App) error {
   492  	if len(args) != 1 {
   493  		return errors.New("Expect 1 argument. See help text for more details.")
   494  	}
   495  
   496  	user := getUserFromUserArg(a, args[0])
   497  	if user == nil {
   498  		return errors.New("Unable to find bot.")
   499  	}
   500  
   501  	_, appErr := a.GetBot(user.Id, false)
   502  	if appErr != nil {
   503  		return fmt.Errorf("Unable to find bot. Error: %s", appErr.Error())
   504  	}
   505  
   506  	password, err := getUpdatedPassword(command)
   507  	if err != nil {
   508  		return err
   509  	}
   510  
   511  	user, err = getUpdatedUserModel(command, a, user)
   512  	if err != nil {
   513  		return err
   514  	}
   515  
   516  	user, appErr = a.UpdateUser(user, false)
   517  	if appErr != nil {
   518  		return fmt.Errorf("Unable to update user. Error: %s" + appErr.Error())
   519  	}
   520  
   521  	appErr = a.UpdatePassword(user, password)
   522  	if appErr != nil {
   523  		return fmt.Errorf("Unable to update password. Error: %s", appErr.Error())
   524  	}
   525  
   526  	systemAdmin, _ := command.Flags().GetBool("system_admin")
   527  	if systemAdmin && !user.IsInRole(model.SYSTEM_ADMIN_ROLE_ID) {
   528  		if _, appErr = a.UpdateUserRoles(
   529  			user.Id,
   530  			fmt.Sprintf("%s %s", user.Roles, model.SYSTEM_ADMIN_ROLE_ID),
   531  			false); appErr != nil {
   532  			return fmt.Errorf("Unable to make user system admin. Error: %s" + appErr.Error())
   533  		}
   534  	}
   535  
   536  	err = a.Srv().Store.Bot().PermanentDelete(user.Id)
   537  	if err != nil {
   538  		return fmt.Errorf("Unable to delete bot. Error: %v", err)
   539  	}
   540  
   541  	CommandPrettyPrintln("id: " + user.Id)
   542  	CommandPrettyPrintln("username: " + user.Username)
   543  	CommandPrettyPrintln("email: " + user.Email)
   544  	CommandPrettyPrintln("nickname: " + user.Nickname)
   545  	CommandPrettyPrintln("first_name: " + user.FirstName)
   546  	CommandPrettyPrintln("last_name: " + user.LastName)
   547  	CommandPrettyPrintln("roles: " + user.Roles)
   548  	CommandPrettyPrintln("locale: " + user.Locale)
   549  
   550  	auditRec := a.MakeAuditRecord("botToUser", audit.Success)
   551  	auditRec.AddMeta("bot", user)
   552  	auditRec.AddMeta("user", user)
   553  	a.LogAuditRec(auditRec, nil)
   554  
   555  	return nil
   556  }
   557  
   558  func userConvertCmdF(command *cobra.Command, args []string) error {
   559  	a, err := InitDBCommandContextCobra(command)
   560  	if err != nil {
   561  		return err
   562  	}
   563  	defer a.Srv().Shutdown()
   564  
   565  	toBot, err := command.Flags().GetBool("bot")
   566  	if err != nil {
   567  		return errors.New("Invalid command. See help text for details.")
   568  	}
   569  
   570  	toUser, err := command.Flags().GetBool("user")
   571  	if err != nil {
   572  		return errors.New("Invalid command. See help text for details.")
   573  	}
   574  
   575  	if !(toUser || toBot) {
   576  		return errors.New("Expect either \"user\" flag or \"bot\" flag. See help text for details.")
   577  	}
   578  
   579  	if toUser && toBot {
   580  		return errors.New("Expect either \"user\" flag or \"bot\" flag but not both. See help text for details.")
   581  	}
   582  
   583  	if toUser {
   584  		return botToUser(command, args, a)
   585  	}
   586  
   587  	usersToBots(args, a)
   588  	return nil
   589  }
   590  
   591  func userInviteCmdF(command *cobra.Command, args []string) error {
   592  	a, err := InitDBCommandContextCobra(command)
   593  	if err != nil {
   594  		return err
   595  	}
   596  	defer a.Srv().Shutdown()
   597  
   598  	if len(args) < 2 {
   599  		return errors.New("Expected at least two arguments. See help text for details.")
   600  	}
   601  
   602  	email := args[0]
   603  	email = strings.ToLower(email)
   604  	if !model.IsValidEmail(email) {
   605  		return errors.New("Invalid email")
   606  	}
   607  
   608  	teams := getTeamsFromTeamArgs(a, args[1:])
   609  	for i, team := range teams {
   610  		err := inviteUser(a, email, team, args[i+1])
   611  
   612  		if err != nil {
   613  			CommandPrintErrorln(err.Error())
   614  		}
   615  	}
   616  
   617  	return nil
   618  }
   619  
   620  func inviteUser(a *app.App, email string, team *model.Team, teamArg string) error {
   621  	invites := []string{email}
   622  	if team == nil {
   623  		return fmt.Errorf("Can't find team '%v'", teamArg)
   624  	}
   625  
   626  	if !*a.Config().ServiceSettings.EnableEmailInvitations {
   627  		return fmt.Errorf("Email invites are disabled.")
   628  	}
   629  
   630  	err := a.Srv().EmailService.SendInviteEmails(team, "Administrator", "Mattermost CLI "+model.NewId(), invites, *a.Config().ServiceSettings.SiteURL)
   631  	if err != nil {
   632  		return err
   633  	}
   634  	CommandPrettyPrintln("Invites may or may not have been sent.")
   635  
   636  	auditRec := a.MakeAuditRecord("inviteUser", audit.Success)
   637  	auditRec.AddMeta("email", email)
   638  	auditRec.AddMeta("team", team)
   639  	a.LogAuditRec(auditRec, nil)
   640  
   641  	return nil
   642  }
   643  
   644  func resetUserPasswordCmdF(command *cobra.Command, args []string) error {
   645  	a, err := InitDBCommandContextCobra(command)
   646  	if err != nil {
   647  		return err
   648  	}
   649  	defer a.Srv().Shutdown()
   650  
   651  	if len(args) != 2 {
   652  		return errors.New("Expected two arguments. See help text for details.")
   653  	}
   654  
   655  	user := getUserFromUserArg(a, args[0])
   656  	if user == nil {
   657  		return errors.New("Unable to find user '" + args[0] + "'")
   658  	}
   659  	password := args[1]
   660  
   661  	if err := a.Srv().Store.User().UpdatePassword(user.Id, model.HashPassword(password)); err != nil {
   662  		return err
   663  	}
   664  
   665  	auditRec := a.MakeAuditRecord("resetUserPassword", audit.Success)
   666  	auditRec.AddMeta("user", user)
   667  	a.LogAuditRec(auditRec, nil)
   668  
   669  	return nil
   670  }
   671  
   672  func updateUserEmailCmdF(command *cobra.Command, args []string) error {
   673  	a, err := InitDBCommandContextCobra(command)
   674  	if err != nil {
   675  		return err
   676  	}
   677  	defer a.Srv().Shutdown()
   678  
   679  	if len(args) != 2 {
   680  		return errors.New("Expected two arguments. See help text for details.")
   681  	}
   682  
   683  	newEmail := args[1]
   684  	newEmail = strings.ToLower(newEmail)
   685  	if !model.IsValidEmail(newEmail) {
   686  		return errors.New("Invalid email: '" + newEmail + "'")
   687  	}
   688  
   689  	if len(args) != 2 {
   690  		return errors.New("Expected two arguments. See help text for details.")
   691  	}
   692  
   693  	user := getUserFromUserArg(a, args[0])
   694  	if user == nil {
   695  		return errors.New("Unable to find user '" + args[0] + "'")
   696  	}
   697  
   698  	user.Email = newEmail
   699  	_, errUpdate := a.UpdateUser(user, true)
   700  	if errUpdate != nil {
   701  		return errors.New(errUpdate.Message)
   702  	}
   703  
   704  	auditRec := a.MakeAuditRecord("updateUserEmail", audit.Success)
   705  	auditRec.AddMeta("user", user)
   706  	auditRec.AddMeta("email", newEmail)
   707  	a.LogAuditRec(auditRec, nil)
   708  
   709  	return nil
   710  }
   711  
   712  func resetUserMfaCmdF(command *cobra.Command, args []string) error {
   713  	a, err := InitDBCommandContextCobra(command)
   714  	if err != nil {
   715  		return err
   716  	}
   717  	defer a.Srv().Shutdown()
   718  
   719  	if len(args) < 1 {
   720  		return errors.New("Expected at least one argument. See help text for details.")
   721  	}
   722  
   723  	users := getUsersFromUserArgs(a, args)
   724  	for i, user := range users {
   725  		if user == nil {
   726  			return errors.New("Unable to find user '" + args[i] + "'")
   727  		}
   728  
   729  		if err := a.DeactivateMfa(user.Id); err != nil {
   730  			return err
   731  		}
   732  
   733  		auditRec := a.MakeAuditRecord("resetUserMfa", audit.Success)
   734  		auditRec.AddMeta("user", user)
   735  		a.LogAuditRec(auditRec, nil)
   736  	}
   737  
   738  	return nil
   739  }
   740  
   741  func deleteUserCmdF(command *cobra.Command, args []string) error {
   742  	a, err := InitDBCommandContextCobra(command)
   743  	if err != nil {
   744  		return err
   745  	}
   746  	defer a.Srv().Shutdown()
   747  
   748  	if len(args) < 1 {
   749  		return errors.New("Expected at least one argument. See help text for details.")
   750  	}
   751  
   752  	confirmFlag, _ := command.Flags().GetBool("confirm")
   753  	if !confirmFlag {
   754  		var confirm string
   755  		CommandPrettyPrintln("Have you performed a database backup? (YES/NO): ")
   756  		fmt.Scanln(&confirm)
   757  
   758  		if confirm != "YES" {
   759  			return errors.New("ABORTED: You did not answer YES exactly, in all capitals.")
   760  		}
   761  		CommandPrettyPrintln("Are you sure you want to permanently delete the specified users? (YES/NO): ")
   762  		fmt.Scanln(&confirm)
   763  		if confirm != "YES" {
   764  			return errors.New("ABORTED: You did not answer YES exactly, in all capitals.")
   765  		}
   766  	}
   767  
   768  	users := getUsersFromUserArgs(a, args)
   769  
   770  	for i, user := range users {
   771  		if user == nil {
   772  			return errors.New("Unable to find user '" + args[i] + "'")
   773  		}
   774  
   775  		if user.IsBot {
   776  			if err := a.PermanentDeleteBot(user.Id); err != nil {
   777  				return err
   778  			}
   779  		} else {
   780  			if err := a.PermanentDeleteUser(&request.Context{}, user); err != nil {
   781  				return err
   782  			}
   783  		}
   784  
   785  		auditRec := a.MakeAuditRecord("deleteUser", audit.Success)
   786  		auditRec.AddMeta("user", user)
   787  		auditRec.AddMeta("isBot", user.IsBot)
   788  		a.LogAuditRec(auditRec, nil)
   789  	}
   790  
   791  	return nil
   792  }
   793  
   794  func deleteAllUsersCommandF(command *cobra.Command, args []string) error {
   795  	a, err := InitDBCommandContextCobra(command)
   796  	if err != nil {
   797  		return err
   798  	}
   799  	defer a.Srv().Shutdown()
   800  
   801  	if len(args) > 0 {
   802  		return errors.New("Expected zero arguments.")
   803  	}
   804  
   805  	confirmFlag, _ := command.Flags().GetBool("confirm")
   806  	if !confirmFlag {
   807  		var confirm string
   808  		CommandPrettyPrintln("Have you performed a database backup? (YES/NO): ")
   809  		fmt.Scanln(&confirm)
   810  
   811  		if confirm != "YES" {
   812  			return errors.New("ABORTED: You did not answer YES exactly, in all capitals.")
   813  		}
   814  		CommandPrettyPrintln("Are you sure you want to permanently delete all user accounts? (YES/NO): ")
   815  		fmt.Scanln(&confirm)
   816  		if confirm != "YES" {
   817  			return errors.New("ABORTED: You did not answer YES exactly, in all capitals.")
   818  		}
   819  	}
   820  
   821  	if err := a.PermanentDeleteAllUsers(&request.Context{}); err != nil {
   822  		return err
   823  	}
   824  	CommandPrettyPrintln("All user accounts successfully deleted.")
   825  
   826  	auditRec := a.MakeAuditRecord("deleteAllUsers", audit.Success)
   827  	a.LogAuditRec(auditRec, nil)
   828  
   829  	return nil
   830  }
   831  
   832  func migrateAuthCmdF(command *cobra.Command, args []string) error {
   833  	if args[1] == "saml" {
   834  		return migrateAuthToSamlCmdF(command, args)
   835  	}
   836  	return migrateAuthToLdapCmdF(command, args)
   837  }
   838  
   839  func migrateAuthToLdapCmdF(command *cobra.Command, args []string) error {
   840  	a, err := InitDBCommandContextCobra(command)
   841  	if err != nil {
   842  		return err
   843  	}
   844  	defer a.Srv().Shutdown()
   845  
   846  	fromAuth := args[0]
   847  	matchField := args[2]
   848  
   849  	if fromAuth == "" || (fromAuth != "email" && fromAuth != "gitlab" && fromAuth != "saml") {
   850  		return errors.New("Invalid from_auth argument")
   851  	}
   852  
   853  	// Email auth in Mattermost system is represented by ""
   854  	if fromAuth == "email" {
   855  		fromAuth = ""
   856  	}
   857  
   858  	if matchField == "" || (matchField != "email" && matchField != "username") {
   859  		return errors.New("Invalid match_field argument")
   860  	}
   861  
   862  	forceFlag, _ := command.Flags().GetBool("force")
   863  	dryRunFlag, _ := command.Flags().GetBool("dryRun")
   864  
   865  	if migrate := a.AccountMigration(); migrate != nil {
   866  		if err := migrate.MigrateToLdap(fromAuth, matchField, forceFlag, dryRunFlag); err != nil {
   867  			return errors.New("Error while migrating users: " + err.Error())
   868  		}
   869  
   870  		CommandPrettyPrintln("Successfully migrated accounts.")
   871  
   872  		if !dryRunFlag {
   873  			auditRec := a.MakeAuditRecord("migrateAuthToLdap", audit.Success)
   874  			auditRec.AddMeta("fromAuth", fromAuth)
   875  			auditRec.AddMeta("matchField", matchField)
   876  			auditRec.AddMeta("force", forceFlag)
   877  			a.LogAuditRec(auditRec, nil)
   878  		}
   879  	}
   880  	return nil
   881  }
   882  
   883  func migrateAuthToSamlCmdF(command *cobra.Command, args []string) error {
   884  	a, err := InitDBCommandContextCobra(command)
   885  	if err != nil {
   886  		return err
   887  	}
   888  	defer a.Srv().Shutdown()
   889  
   890  	dryRunFlag, _ := command.Flags().GetBool("dryRun")
   891  	autoFlag, _ := command.Flags().GetBool("auto")
   892  
   893  	matchesFile := ""
   894  	matches := map[string]string{}
   895  	if !autoFlag {
   896  		matchesFile = args[2]
   897  
   898  		file, e := ioutil.ReadFile(matchesFile)
   899  		if e != nil {
   900  			return errors.New("Invalid users file.")
   901  		}
   902  		if json.Unmarshal(file, &matches) != nil {
   903  			return errors.New("Invalid users file.")
   904  		}
   905  	}
   906  
   907  	fromAuth := args[0]
   908  
   909  	if fromAuth == "" || (fromAuth != "email" && fromAuth != "gitlab" && fromAuth != "ldap") {
   910  		return errors.New("Invalid from_auth argument")
   911  	}
   912  
   913  	if autoFlag && !dryRunFlag {
   914  		var confirm string
   915  		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):")
   916  		fmt.Scanln(&confirm)
   917  
   918  		if confirm != "YES" {
   919  			return errors.New("ABORTED: You did not answer YES exactly, in all capitals.")
   920  		}
   921  	}
   922  
   923  	// Email auth in Mattermost system is represented by ""
   924  	if fromAuth == "email" {
   925  		fromAuth = ""
   926  	}
   927  
   928  	if migrate := a.AccountMigration(); migrate != nil {
   929  		if err := migrate.MigrateToSaml(fromAuth, matches, autoFlag, dryRunFlag); err != nil {
   930  			return errors.New("Error while migrating users: " + err.Error())
   931  		}
   932  
   933  		CommandPrettyPrintln("Successfully migrated accounts.")
   934  
   935  		if !dryRunFlag {
   936  			auditRec := a.MakeAuditRecord("migrateAuthToSaml", audit.Success)
   937  			auditRec.AddMeta("auto", autoFlag)
   938  			a.LogAuditRec(auditRec, nil)
   939  		}
   940  	}
   941  	return nil
   942  }
   943  
   944  func verifyUserCmdF(command *cobra.Command, args []string) error {
   945  	a, err := InitDBCommandContextCobra(command)
   946  	if err != nil {
   947  		return err
   948  	}
   949  	defer a.Srv().Shutdown()
   950  
   951  	if len(args) < 1 {
   952  		return errors.New("Expected at least one argument. See help text for details.")
   953  	}
   954  
   955  	users := getUsersFromUserArgs(a, args)
   956  
   957  	for i, user := range users {
   958  		if user == nil {
   959  			CommandPrintErrorln("Unable to find user '" + args[i] + "'")
   960  			continue
   961  		}
   962  		if _, err := a.Srv().Store.User().VerifyEmail(user.Id, user.Email); err != nil {
   963  			CommandPrintErrorln("Unable to verify '" + args[i] + "' email. Error: " + err.Error())
   964  		}
   965  	}
   966  
   967  	return nil
   968  }
   969  
   970  func searchUserCmdF(command *cobra.Command, args []string) error {
   971  	a, err := InitDBCommandContextCobra(command)
   972  	if err != nil {
   973  		return err
   974  	}
   975  	defer a.Srv().Shutdown()
   976  
   977  	if len(args) < 1 {
   978  		return errors.New("Expected at least one argument. See help text for details.")
   979  	}
   980  
   981  	users := getUsersFromUserArgs(a, args)
   982  
   983  	for i, user := range users {
   984  		if i > 0 {
   985  			CommandPrettyPrintln("------------------------------")
   986  		}
   987  		if user == nil {
   988  			CommandPrintErrorln("Unable to find user '" + args[i] + "'")
   989  			continue
   990  		}
   991  
   992  		CommandPrettyPrintln("id: " + user.Id)
   993  		CommandPrettyPrintln("username: " + user.Username)
   994  		CommandPrettyPrintln("nickname: " + user.Nickname)
   995  		CommandPrettyPrintln("position: " + user.Position)
   996  		CommandPrettyPrintln("first_name: " + user.FirstName)
   997  		CommandPrettyPrintln("last_name: " + user.LastName)
   998  		CommandPrettyPrintln("email: " + user.Email)
   999  		CommandPrettyPrintln("auth_service: " + user.AuthService)
  1000  	}
  1001  
  1002  	return nil
  1003  }