diff options
author | Corey Hulen <corey@hulen.com> | 2016-04-21 22:37:01 -0700 |
---|---|---|
committer | Corey Hulen <corey@hulen.com> | 2016-04-21 22:37:01 -0700 |
commit | 2e5617c29be69637acd384e85f795a0b343bec8d (patch) | |
tree | 6b8bdae1e664013b97c2dda94985375abda91aa5 /mattermost.go | |
parent | 5c755463ed3a4c74a383fb4460b5be02d8868481 (diff) | |
download | chat-2e5617c29be69637acd384e85f795a0b343bec8d.tar.gz chat-2e5617c29be69637acd384e85f795a0b343bec8d.tar.bz2 chat-2e5617c29be69637acd384e85f795a0b343bec8d.zip |
PLT-2057 User as a first class object (#2648)
* Adding TeamMember to system
* Fixing all unit tests on the backend
* Fixing merge conflicts
* Fixing merge conflict
* Adding javascript unit tests
* Adding TeamMember to system
* Fixing all unit tests on the backend
* Fixing merge conflicts
* Fixing merge conflict
* Adding javascript unit tests
* Adding client side unit test
* Cleaning up the clint side tests
* Fixing msg
* Adding more client side unit tests
* Adding more using tests
* Adding last bit of client side unit tests and adding make cmd
* Fixing bad merge
* Fixing libraries
* Updating to new client side API
* Fixing borken unit test
* Fixing unit tests
* ugg...trying to beat gofmt
* ugg...trying to beat gofmt
* Cleaning up remainder of the server side routes
* Adding inital load api
* Increased coverage of webhook unit tests (#2660)
* Adding loading ... to root html
* Fixing bad merge
* Removing explicit content type so superagent will guess corectly (#2685)
* Fixing merge and unit tests
* Adding create team UI
* Fixing signup flows
* Adding LDAP unit tests and enterprise unit test helper (#2702)
* Add the ability to reset MFA from the commandline (#2706)
* Fixing compliance unit tests
* Fixing client side tests
* Adding open server to system console
* Moving websocket connection
* Fixing unit test
* Fixing unit tests
* Fixing unit tests
* Adding nickname and more LDAP unit tests (#2717)
* Adding join open teams
* Cleaning up all TODOs in the code
* Fixing web sockets
* Removing unused webockets file
* PLT-2533 Add the ability to reset a user's MFA from the system console (#2715)
* Add the ability to reset a user's MFA from the system console
* Add client side unit test for adminResetMfa
* Reorganizing authentication to fix LDAP error message (#2723)
* Fixing failing unit test
* Initial upgrade db code
* Adding upgrade script
* Fixing upgrade script after running on core
* Update OAuth and Claim routes to work with user model changes (#2739)
* Fixing perminant deletion. Adding ability to delete all user and the entire database (#2740)
* Fixing team invite ldap login call (#2741)
* Fixing bluebar and some img stuff
* Fix all the different file upload web utils (#2743)
* Fixing invalid session redirect (#2744)
* Redirect on bad channel name (#2746)
* Fixing a bunch of issue and removing dead code
* Patch to fix error message on leave channel (#2747)
* Setting EnableOpenServer to false by default
* Fixing config
* Fixing upgrade
* Fixing reported bugs
* Bug fixes for PLT-2057
* PLT-2563 Redo password recovery to use a database table (#2745)
* Redo password recovery to use a database table
* Update reset password audits
* Split out admin and user reset password APIs to be separate
* Delete password recovery when user is permanently deleted
* Consolidate password resetting into a single function
* Removed private channels as an option for outgoing webhooks (#2752)
* PLT-2577/PLT-2552 Fixes for backstage (#2753)
* Added URL to incoming webhook list
* Fixed client functions for adding/removing integrations
* Disallowed slash commands without trigger words
* Fixed clientside handling of errors on AddCommand page
* Minor auth cleanup (#2758)
* Changed EditPostModal to just close if you save without making any changes (#2759)
* Renamed client -> Client in async_client.jsx and fixed eslint warnings (#2756)
* Fixed url in channel info modal (#2755)
* Fixing reported issues
* Moving to version 3 of the apis
* Fixing command unit tests (#2760)
* Adding team admins
* Fixing DM issue
* Fixing eslint error
* Properly set EditPostModal's originalText state in all cases (#2762)
* Update client config check to assume features is defined if server is licensed (#2772)
* Fixing url link
* Fixing issue with websocket crashing when sending messages to different teams
Diffstat (limited to 'mattermost.go')
-rw-r--r-- | mattermost.go | 430 |
1 files changed, 393 insertions, 37 deletions
diff --git a/mattermost.go b/mattermost.go index a417fb3ec..a16bf53a7 100644 --- a/mattermost.go +++ b/mattermost.go @@ -22,6 +22,7 @@ import ( "github.com/mattermost/platform/einterfaces" "github.com/mattermost/platform/manualtesting" "github.com/mattermost/platform/model" + "github.com/mattermost/platform/store" "github.com/mattermost/platform/utils" "github.com/mattermost/platform/web" @@ -36,13 +37,18 @@ import ( //ENTERPRISE_IMPORTS +var flagCmdUpdateDb30 bool var flagCmdCreateTeam bool var flagCmdCreateUser bool var flagCmdAssignRole bool var flagCmdVersion bool var flagCmdResetPassword bool +var flagCmdResetMfa bool var flagCmdPermanentDeleteUser bool var flagCmdPermanentDeleteTeam bool +var flagCmdPermanentDeleteAllUsers bool +var flagCmdResetDatabase bool +var flagUsername string var flagCmdUploadLicense bool var flagConfigFile string var flagLicenseFile string @@ -69,6 +75,10 @@ func main() { l4g.Info(utils.T("mattermost.working_dir"), pwd) l4g.Info(utils.T("mattermost.config_file"), utils.FindConfigFile(flagConfigFile)) + // Speical case for upgrading the db to 3.0 + // ADDED for 3.0 REMOVE for 3.4 + cmdUpdateDb30() + api.NewServer() api.InitApi() web.InitWeb() @@ -223,19 +233,24 @@ func parseCmds() { } flag.StringVar(&flagConfigFile, "config", "config.json", "") + flag.StringVar(&flagUsername, "username", "", "") flag.StringVar(&flagLicenseFile, "license", "", "") flag.StringVar(&flagEmail, "email", "", "") flag.StringVar(&flagPassword, "password", "", "") flag.StringVar(&flagTeamName, "team_name", "", "") flag.StringVar(&flagRole, "role", "", "") + flag.BoolVar(&flagCmdUpdateDb30, "upgrade_db_30", false, "") flag.BoolVar(&flagCmdCreateTeam, "create_team", false, "") flag.BoolVar(&flagCmdCreateUser, "create_user", false, "") flag.BoolVar(&flagCmdAssignRole, "assign_role", false, "") flag.BoolVar(&flagCmdVersion, "version", false, "") flag.BoolVar(&flagCmdResetPassword, "reset_password", false, "") + flag.BoolVar(&flagCmdResetMfa, "reset_mfa", false, "") flag.BoolVar(&flagCmdPermanentDeleteUser, "permanent_delete_user", false, "") flag.BoolVar(&flagCmdPermanentDeleteTeam, "permanent_delete_team", false, "") + flag.BoolVar(&flagCmdPermanentDeleteAllUsers, "permanent_delete_all_users", false, "") + flag.BoolVar(&flagCmdResetDatabase, "reset_database", false, "") flag.BoolVar(&flagCmdUploadLicense, "upload_license", false, "") flag.Parse() @@ -244,9 +259,12 @@ func parseCmds() { flagCmdCreateUser || flagCmdAssignRole || flagCmdResetPassword || + flagCmdResetMfa || flagCmdVersion || flagCmdPermanentDeleteUser || flagCmdPermanentDeleteTeam || + flagCmdPermanentDeleteAllUsers || + flagCmdResetDatabase || flagCmdUploadLicense) } @@ -256,11 +274,258 @@ func runCmds() { cmdCreateUser() cmdAssignRole() cmdResetPassword() + cmdResetMfa() cmdPermDeleteUser() cmdPermDeleteTeam() + cmdPermDeleteAllUsers() + cmdResetDatabase() cmdUploadLicense() } +type TeamForUpgrade struct { + Id string + Name string +} + +// ADDED for 3.0 REMOVE for 3.4 +func cmdUpdateDb30() { + if flagCmdUpdateDb30 { + api.Srv = &api.Server{} + api.Srv.Store = store.NewSqlStoreForUpgrade30() + store := api.Srv.Store.(*store.SqlStore) + + l4g.Info("Attempting to run speical upgrade of the database schema to version 3.0 for user model changes") + time.Sleep(time.Second) + + if !store.DoesColumnExist("Users", "TeamId") { + fmt.Println("**WARNING** the database schema appears to be upgraded to 3.0") + flushLogAndExit(1) + } + + if !(store.SchemaVersion == "2.2.0" || + store.SchemaVersion == "2.1.0" || + store.SchemaVersion == "2.0.0") { + fmt.Println("**WARNING** the database schema needs to be version 2.2.0, 2.1.0 or 2.0.0 to upgrade") + flushLogAndExit(1) + } + + var confirmBackup string + fmt.Println("\nPlease see http://www.mattermost.org/upgrade-to-3-0/") + fmt.Println("**WARNING** This upgrade process will be irreversible.") + fmt.Print("Have you performed a database backup? (YES/NO): ") + fmt.Scanln(&confirmBackup) + if confirmBackup != "YES" { + fmt.Fprintln(os.Stderr, "ABORTED: You did not answer YES exactly, in all capitals.") + flushLogAndExit(1) + } + + var flagTeamName string + var teams []*TeamForUpgrade + + if _, err := store.GetMaster().Select(&teams, "SELECT Id, Name FROM Teams"); err != nil { + l4g.Error("Failed to load all teams details=%v", err) + flushLogAndExit(1) + } + + fmt.Println(fmt.Sprintf("We found %v teams.", len(teams))) + + for _, team := range teams { + fmt.Println(team.Name) + } + + fmt.Print("Please pick a primary team from the list above: ") + fmt.Scanln(&flagTeamName) + + var team *TeamForUpgrade + for _, t := range teams { + if t.Name == flagTeamName { + team = t + break + } + } + + if team == nil { + l4g.Error("Failed to find primary team details") + flushLogAndExit(1) + } + + l4g.Info("Starting speical 3.0 database upgrade with performed_backup=YES team_name=%v", team.Name) + l4g.Info("Primary team %v will be left unchanged", team.Name) + l4g.Info("Upgrading primary team %v", team.Name) + + uniqueEmails := make(map[string]bool) + uniqueUsernames := make(map[string]bool) + primaryUsers := convertTeamTo30(team, uniqueEmails, uniqueUsernames) + + l4g.Info("Upgraded %v users", len(primaryUsers)) + + for _, otherTeam := range teams { + if otherTeam.Id != team.Id { + l4g.Info("Upgrading team %v", otherTeam.Name) + users := convertTeamTo30(otherTeam, uniqueEmails, uniqueUsernames) + l4g.Info("Upgraded %v users", len(users)) + + } + } + + l4g.Info("Altering other scheme changes needed 3.0 for user model changes") + + if _, err := store.GetMaster().Exec(` + UPDATE Channels + SET + TeamId = '' + WHERE + Type = 'D' + `, + ); err != nil { + l4g.Error("Failed to update direct channel types details=%v", err) + flushLogAndExit(1) + } + + extraLength := store.GetMaxLengthOfColumnIfExists("Audits", "ExtraInfo") + if len(extraLength) > 0 && extraLength != "1024" { + store.AlterColumnTypeIfExists("Audits", "ExtraInfo", "VARCHAR(1024)", "VARCHAR(1024)") + } + + actionLength := store.GetMaxLengthOfColumnIfExists("Audits", "Action") + if len(actionLength) > 0 && actionLength != "512" { + store.AlterColumnTypeIfExists("Audits", "Action", "VARCHAR(512)", "VARCHAR(512)") + } + + if store.DoesColumnExist("Sessions", "TeamId") { + store.RemoveColumnIfExists("Sessions", "TeamId") + store.GetMaster().Exec(`TRUNCATE Sessions`) + } + + // ADDED for 2.2 REMOVE for 2.6 + store.CreateColumnIfNotExists("Users", "MfaActive", "tinyint(1)", "boolean", "0") + store.CreateColumnIfNotExists("Users", "MfaSecret", "varchar(128)", "character varying(128)", "") + + // ADDED for 2.2 REMOVE for 2.6 + if store.DoesColumnExist("Users", "TeamId") { + store.RemoveIndexIfExists("idx_users_team_id", "Users") + store.CreateUniqueIndexIfNotExists("idx_users_email_unique", "Users", "Email") + store.CreateUniqueIndexIfNotExists("idx_users_username_unique", "Users", "Username") + store.RemoveColumnIfExists("Teams", "AllowTeamListing") + store.RemoveColumnIfExists("Users", "TeamId") + } + + l4g.Info("Finished running speical upgrade of the database schema to version 3.0 for user model changes") + + if result := <-store.System().Update(&model.System{Name: "Version", Value: model.CurrentVersion}); result.Err != nil { + l4g.Error("Failed to update system schema version details=%v", result.Err) + flushLogAndExit(1) + } + + l4g.Info(utils.T("store.sql.upgraded.warn"), model.CurrentVersion) + fmt.Println("**SUCCESS** with upgrade") + + flushLogAndExit(0) + } +} + +type UserForUpgrade struct { + Id string + Username string + Email string + Roles string + TeamId string +} + +func convertTeamTo30(team *TeamForUpgrade, uniqueEmails map[string]bool, uniqueUsernames map[string]bool) []*UserForUpgrade { + store := api.Srv.Store.(*store.SqlStore) + var users []*UserForUpgrade + if _, err := store.GetMaster().Select(&users, "SELECT Users.Id, Users.Username, Users.Email, Users.Roles, Users.TeamId FROM Users WHERE Users.TeamId = :TeamId", map[string]interface{}{"TeamId": team.Id}); err != nil { + l4g.Error("Failed to load profiles for team details=%v", err) + flushLogAndExit(1) + } + + var members []*model.TeamMember + if result := <-api.Srv.Store.Team().GetMembers(team.Id); result.Err != nil { + l4g.Error("Failed to load team membership details=%v", result.Err) + flushLogAndExit(1) + } else { + members = result.Data.([]*model.TeamMember) + } + + for _, user := range users { + shouldUpdateUser := false + previousRole := user.Roles + previousEmail := user.Email + previousUsername := user.Username + + member := &model.TeamMember{ + TeamId: team.Id, + UserId: user.Id, + } + + if model.IsInRole(user.Roles, model.ROLE_TEAM_ADMIN) { + member.Roles = model.ROLE_TEAM_ADMIN + user.Roles = "" + shouldUpdateUser = true + } + + exists := false + for _, member := range members { + if member.UserId == user.Id { + exists = true + break + } + } + + if !exists { + if result := <-api.Srv.Store.Team().SaveMember(member); result.Err != nil { + l4g.Error("Failed to save membership for %v details=%v", user.Email, result.Err) + flushLogAndExit(1) + } + } + + if uniqueEmails[user.Email] { + shouldUpdateUser = true + emailParts := strings.Split(user.Email, "@") + if len(emailParts) == 2 { + user.Email = emailParts[0] + "+" + team.Name + "@" + emailParts[1] + } else { + user.Email = user.Email + "." + team.Name + } + } + + if uniqueUsernames[user.Username] { + shouldUpdateUser = true + user.Username = user.Username + "." + team.Name + } + + if shouldUpdateUser { + if _, err := store.GetMaster().Exec(` + UPDATE Users + SET + Email = :Email, + Username = :Username, + Roles = :Roles + WHERE + Id = :Id + `, + map[string]interface{}{ + "Email": user.Email, + "Username": user.Username, + "Roles": user.Roles, + "Id": user.Id, + }, + ); err != nil { + l4g.Error("Failed to update user %v details=%v", user.Email, err) + flushLogAndExit(1) + } + + l4g.Info("modified user_id=%v, changed email from=%v to=%v, changed username from=%v to %v changed roles from=%v to=%v", user.Id, previousEmail, user.Email, previousUsername, user.Username, previousRole, user.Roles) + } + + uniqueEmails[user.Email] = true + uniqueUsernames[user.Username] = true + } + + return users +} + func cmdCreateTeam() { if flagCmdCreateTeam { if len(flagTeamName) == 0 { @@ -327,10 +592,9 @@ func cmdCreateUser() { flushLogAndExit(1) } else { team = result.Data.(*model.Team) - user.TeamId = team.Id } - _, err := api.CreateUser(team, user) + ruser, err := api.CreateUser(user) if err != nil { if err.Id != "store.sql_user.save.email_exists.app_error" { l4g.Error("%v", err) @@ -338,6 +602,12 @@ func cmdCreateUser() { } } + err = api.JoinUserToTeam(team, ruser) + if err != nil { + l4g.Error("%v", err) + flushLogAndExit(1) + } + os.Exit(0) } } @@ -368,7 +638,7 @@ func cmdAssignRole() { os.Exit(1) } - if !model.IsValidRoles(flagRole) { + if !model.IsValidUserRoles(flagRole) { fmt.Fprintln(os.Stderr, "flag invalid argument: -role") flag.Usage() os.Exit(1) @@ -376,16 +646,8 @@ func cmdAssignRole() { c := getMockContext() - var team *model.Team - if result := <-api.Srv.Store.Team().GetByName(flagTeamName); result.Err != nil { - l4g.Error("%v", result.Err) - flushLogAndExit(1) - } else { - team = result.Data.(*model.Team) - } - var user *model.User - if result := <-api.Srv.Store.User().GetByEmail(team.Id, flagEmail); result.Err != nil { + if result := <-api.Srv.Store.User().GetByEmail(flagEmail); result.Err != nil { l4g.Error("%v", result.Err) flushLogAndExit(1) } else { @@ -426,16 +688,8 @@ func cmdResetPassword() { os.Exit(1) } - var team *model.Team - if result := <-api.Srv.Store.Team().GetByName(flagTeamName); result.Err != nil { - l4g.Error("%v", result.Err) - flushLogAndExit(1) - } else { - team = result.Data.(*model.Team) - } - var user *model.User - if result := <-api.Srv.Store.User().GetByEmail(team.Id, flagEmail); result.Err != nil { + if result := <-api.Srv.Store.User().GetByEmail(flagEmail); result.Err != nil { l4g.Error("%v", result.Err) flushLogAndExit(1) } else { @@ -451,14 +705,42 @@ func cmdResetPassword() { } } -func cmdPermDeleteUser() { - if flagCmdPermanentDeleteUser { - if len(flagTeamName) == 0 { - fmt.Fprintln(os.Stderr, "flag needs an argument: -team_name") +func cmdResetMfa() { + if flagCmdResetMfa { + if len(flagEmail) == 0 && len(flagUsername) == 0 { + fmt.Fprintln(os.Stderr, "flag needs an argument: -email OR -username") flag.Usage() os.Exit(1) } + var user *model.User + if len(flagEmail) > 0 { + if result := <-api.Srv.Store.User().GetByEmail(flagEmail); result.Err != nil { + l4g.Error("%v", result.Err) + flushLogAndExit(1) + } else { + user = result.Data.(*model.User) + } + } else { + if result := <-api.Srv.Store.User().GetByUsername(flagUsername); result.Err != nil { + l4g.Error("%v", result.Err) + flushLogAndExit(1) + } else { + user = result.Data.(*model.User) + } + } + + if err := api.DeactivateMfa(user.Id); err != nil { + l4g.Error("%v", err) + flushLogAndExit(1) + } + + os.Exit(0) + } +} + +func cmdPermDeleteUser() { + if flagCmdPermanentDeleteUser { if len(flagEmail) == 0 { fmt.Fprintln(os.Stderr, "flag needs an argument: -email") flag.Usage() @@ -467,16 +749,8 @@ func cmdPermDeleteUser() { c := getMockContext() - var team *model.Team - if result := <-api.Srv.Store.Team().GetByName(flagTeamName); result.Err != nil { - l4g.Error("%v", result.Err) - flushLogAndExit(1) - } else { - team = result.Data.(*model.Team) - } - var user *model.User - if result := <-api.Srv.Store.User().GetByEmail(team.Id, flagEmail); result.Err != nil { + if result := <-api.Srv.Store.User().GetByEmail(flagEmail); result.Err != nil { l4g.Error("%v", result.Err) flushLogAndExit(1) } else { @@ -487,6 +761,7 @@ func cmdPermDeleteUser() { fmt.Print("Have you performed a database backup? (YES/NO): ") fmt.Scanln(&confirmBackup) if confirmBackup != "YES" { + fmt.Print("ABORTED: You did not answer YES exactly, in all capitals.") flushLogAndExit(1) } @@ -494,6 +769,7 @@ func cmdPermDeleteUser() { fmt.Printf("Are you sure you want to delete the user %v? All data will be permanently deleted? (YES/NO): ", user.Email) fmt.Scanln(&confirm) if confirm != "YES" { + fmt.Print("ABORTED: You did not answer YES exactly, in all capitals.") flushLogAndExit(1) } @@ -501,6 +777,7 @@ func cmdPermDeleteUser() { l4g.Error("%v", err) flushLogAndExit(1) } else { + fmt.Print("SUCCESS: User deleted.") flushLogAndExit(0) } } @@ -528,6 +805,7 @@ func cmdPermDeleteTeam() { fmt.Print("Have you performed a database backup? (YES/NO): ") fmt.Scanln(&confirmBackup) if confirmBackup != "YES" { + fmt.Print("ABORTED: You did not answer YES exactly, in all capitals.") flushLogAndExit(1) } @@ -535,6 +813,7 @@ func cmdPermDeleteTeam() { fmt.Printf("Are you sure you want to delete the team %v? All data will be permanently deleted? (YES/NO): ", team.Name) fmt.Scanln(&confirm) if confirm != "YES" { + fmt.Print("ABORTED: You did not answer YES exactly, in all capitals.") flushLogAndExit(1) } @@ -542,11 +821,67 @@ func cmdPermDeleteTeam() { l4g.Error("%v", err) flushLogAndExit(1) } else { + fmt.Print("SUCCESS: Team deleted.") flushLogAndExit(0) } } } +func cmdPermDeleteAllUsers() { + if flagCmdPermanentDeleteAllUsers { + c := getMockContext() + + var confirmBackup string + fmt.Print("Have you performed a database backup? (YES/NO): ") + fmt.Scanln(&confirmBackup) + if confirmBackup != "YES" { + fmt.Print("ABORTED: You did not answer YES exactly, in all capitals.") + flushLogAndExit(1) + } + + var confirm string + fmt.Printf("Are you sure you want to delete all the users? All data will be permanently deleted? (YES/NO): ") + fmt.Scanln(&confirm) + if confirm != "YES" { + fmt.Print("ABORTED: You did not answer YES exactly, in all capitals.") + flushLogAndExit(1) + } + + if err := api.PermanentDeleteAllUsers(c); err != nil { + l4g.Error("%v", err) + flushLogAndExit(1) + } else { + fmt.Print("SUCCESS: All users deleted.") + flushLogAndExit(0) + } + } +} + +func cmdResetDatabase() { + if flagCmdResetDatabase { + var confirmBackup string + fmt.Print("Have you performed a database backup? (YES/NO): ") + fmt.Scanln(&confirmBackup) + if confirmBackup != "YES" { + fmt.Print("ABORTED: You did not answer YES exactly, in all capitals.") + flushLogAndExit(1) + } + + var confirm string + fmt.Printf("Are you sure you want to delete everything? ALL data will be permanently deleted? (YES/NO): ") + fmt.Scanln(&confirm) + if confirm != "YES" { + fmt.Print("ABORTED: You did not answer YES exactly, in all capitals.") + flushLogAndExit(1) + } + + api.Srv.Store.DropAllTables() + fmt.Print("SUCCESS: Database reset.") + flushLogAndExit(0) + } + +} + func cmdUploadLicense() { if flagCmdUploadLicense { if model.BuildEnterpriseReady != "true" { @@ -574,7 +909,7 @@ func cmdUploadLicense() { flushLogAndExit(0) } - os.Exit(0) + flushLogAndExit(0) } } @@ -604,6 +939,8 @@ USAGE: FLAGS: -config="config.json" Path to the config file + -username="someuser" Username used in other commands + -license="ex.mattermost-license" Path to your license file -email="user@example.com" Email address used in other commands @@ -644,14 +981,33 @@ COMMANDS: Example: platform -reset_password -team_name="name" -email="user@example.com" -password="newpassword" + -reset_mfa Turns off multi-factor authentication for a user. It requires the + -email or -username flag. + Example: + platform -reset_mfa -username="someuser" + + -reset_database Completely erases the database causing the loss of all data. This + will reset Mattermost to it's initial state. (note this will not + erase your configuration.) + + Example: + platform -reset_mfa -username="someuser" + -permanent_delete_user Permanently deletes a user and all related information including posts from the database. It requires the + -email flag. You may need to restart the + server to invalidate the cache + Example: + platform -permanent_delete_user -email="user@example.com" + + -permanent_delete_all_users Permanently deletes all users and all related information + including posts from the database. It requires the -team_name, and -email flag. You may need to restart the server to invalidate the cache Example: - platform -permanent_delete_user -team_name="name" -email="user@example.com" + platform -permanent_delete_all_users -team_name="name" -email="user@example.com" - -permanent_delete_team Permanently deletes a team and all users along with + -permanent_delete_team Permanently deletes a team allong with all related information including posts from the database. It requires the -team_name flag. You may need to restart the server to invalidate the cache. |