summaryrefslogtreecommitdiffstats
path: root/mattermost.go
diff options
context:
space:
mode:
Diffstat (limited to 'mattermost.go')
-rw-r--r--mattermost.go430
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.