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 }