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