From 2e5617c29be69637acd384e85f795a0b343bec8d Mon Sep 17 00:00:00 2001 From: Corey Hulen Date: Thu, 21 Apr 2016 22:37:01 -0700 Subject: 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 --- api/admin.go | 83 ++- api/admin_test.go | 324 ++++------ api/api.go | 78 ++- api/api_test.go | 47 -- api/apitestlib.go | 223 +++++++ api/authentication.go | 99 +++ api/auto_channels.go | 12 +- api/auto_environment.go | 12 +- api/auto_posts.go | 2 +- api/auto_teams.go | 4 +- api/auto_users.go | 31 +- api/channel.go | 101 ++- api/channel_benchmark_test.go | 115 ++-- api/channel_test.go | 368 +++-------- api/command.go | 44 +- api/command_echo_test.go | 19 +- api/command_join.go | 2 +- api/command_join_test.go | 25 +- api/command_loadtest.go | 35 +- api/command_loadtest_test.go | 124 +--- api/command_logout_test.go | 17 +- api/command_me_test.go | 23 +- api/command_msg.go | 8 +- api/command_msg_test.go | 40 +- api/command_shrug_test.go | 23 +- api/command_test.go | 119 +--- api/context.go | 157 +++-- api/context_test.go | 51 +- api/export.go | 2 +- api/file.go | 34 +- api/file_benchmark_test.go | 13 +- api/file_test.go | 124 ++-- api/import.go | 6 +- api/license.go | 10 +- api/license_test.go | 3 +- api/oauth.go | 256 ++++++-- api/oauth_test.go | 26 +- api/post.go | 110 ++-- api/post_benchmark_test.go | 30 +- api/post_test.go | 385 +++-------- api/preference.go | 13 +- api/preference_test.go | 75 +-- api/server.go | 4 +- api/sharding.go | 79 --- api/slackimport.go | 3 +- api/team.go | 461 ++++++++------ api/team_test.go | 245 ++++++- api/user.go | 1401 +++++++++++++++++------------------------ api/user_test.go | 571 +++++++++-------- api/web_conn.go | 78 ++- api/web_hub.go | 128 ++-- api/web_socket.go | 7 +- api/web_socket_test.go | 44 +- api/web_team_hub.go | 123 ---- api/webhook.go | 50 +- api/webhook_test.go | 733 +++++++++++++-------- 56 files changed, 3573 insertions(+), 3627 deletions(-) delete mode 100644 api/api_test.go create mode 100644 api/apitestlib.go create mode 100644 api/authentication.go delete mode 100644 api/sharding.go delete mode 100644 api/web_team_hub.go (limited to 'api') diff --git a/api/admin.go b/api/admin.go index 3ed2bee7a..930170619 100644 --- a/api/admin.go +++ b/api/admin.go @@ -19,24 +19,25 @@ import ( "github.com/mssola/user_agent" ) -func InitAdmin(r *mux.Router) { +func InitAdmin() { l4g.Debug(utils.T("api.admin.init.debug")) - sr := r.PathPrefix("/admin").Subrouter() - sr.Handle("/logs", ApiUserRequired(getLogs)).Methods("GET") - sr.Handle("/audits", ApiUserRequired(getAllAudits)).Methods("GET") - sr.Handle("/config", ApiUserRequired(getConfig)).Methods("GET") - sr.Handle("/save_config", ApiUserRequired(saveConfig)).Methods("POST") - sr.Handle("/test_email", ApiUserRequired(testEmail)).Methods("POST") - sr.Handle("/client_props", ApiAppHandler(getClientConfig)).Methods("GET") - sr.Handle("/log_client", ApiAppHandler(logClient)).Methods("POST") - sr.Handle("/analytics/{id:[A-Za-z0-9]+}/{name:[A-Za-z0-9_]+}", ApiUserRequired(getAnalytics)).Methods("GET") - sr.Handle("/analytics/{name:[A-Za-z0-9_]+}", ApiUserRequired(getAnalytics)).Methods("GET") - sr.Handle("/save_compliance_report", ApiUserRequired(saveComplianceReport)).Methods("POST") - sr.Handle("/compliance_reports", ApiUserRequired(getComplianceReports)).Methods("GET") - sr.Handle("/download_compliance_report/{id:[A-Za-z0-9]+}", ApiUserRequired(downloadComplianceReport)).Methods("GET") - sr.Handle("/upload_brand_image", ApiAdminSystemRequired(uploadBrandImage)).Methods("POST") - sr.Handle("/get_brand_image", ApiAppHandlerTrustRequester(getBrandImage)).Methods("GET") + BaseRoutes.Admin.Handle("/logs", ApiUserRequired(getLogs)).Methods("GET") + BaseRoutes.Admin.Handle("/audits", ApiUserRequired(getAllAudits)).Methods("GET") + BaseRoutes.Admin.Handle("/config", ApiUserRequired(getConfig)).Methods("GET") + BaseRoutes.Admin.Handle("/save_config", ApiUserRequired(saveConfig)).Methods("POST") + BaseRoutes.Admin.Handle("/test_email", ApiUserRequired(testEmail)).Methods("POST") + BaseRoutes.Admin.Handle("/client_props", ApiAppHandler(getClientConfig)).Methods("GET") + BaseRoutes.Admin.Handle("/log_client", ApiAppHandler(logClient)).Methods("POST") + BaseRoutes.Admin.Handle("/analytics/{id:[A-Za-z0-9]+}/{name:[A-Za-z0-9_]+}", ApiUserRequired(getAnalytics)).Methods("GET") + BaseRoutes.Admin.Handle("/analytics/{name:[A-Za-z0-9_]+}", ApiUserRequired(getAnalytics)).Methods("GET") + BaseRoutes.Admin.Handle("/save_compliance_report", ApiUserRequired(saveComplianceReport)).Methods("POST") + BaseRoutes.Admin.Handle("/compliance_reports", ApiUserRequired(getComplianceReports)).Methods("GET") + BaseRoutes.Admin.Handle("/download_compliance_report/{id:[A-Za-z0-9]+}", ApiUserRequired(downloadComplianceReport)).Methods("GET") + BaseRoutes.Admin.Handle("/upload_brand_image", ApiAdminSystemRequired(uploadBrandImage)).Methods("POST") + BaseRoutes.Admin.Handle("/get_brand_image", ApiAppHandlerTrustRequester(getBrandImage)).Methods("GET") + BaseRoutes.Admin.Handle("/reset_mfa", ApiAdminSystemRequired(adminResetMfa)).Methods("POST") + BaseRoutes.Admin.Handle("/reset_password", ApiAdminSystemRequired(adminResetPassword)).Methods("POST") } func getLogs(c *Context, w http.ResponseWriter, r *http.Request) { @@ -374,7 +375,7 @@ func getAnalytics(c *Context, w http.ResponseWriter, r *http.Request) { iHookChan := Srv.Store.Webhook().AnalyticsIncomingCount(teamId) oHookChan := Srv.Store.Webhook().AnalyticsOutgoingCount(teamId) commandChan := Srv.Store.Command().AnalyticsCommandCount(teamId) - sessionChan := Srv.Store.Session().AnalyticsSessionCount(teamId) + sessionChan := Srv.Store.Session().AnalyticsSessionCount() if r := <-fileChan; r.Err != nil { c.Err = r.Err @@ -498,3 +499,51 @@ func getBrandImage(c *Context, w http.ResponseWriter, r *http.Request) { w.Write(img) } } + +func adminResetMfa(c *Context, w http.ResponseWriter, r *http.Request) { + props := model.MapFromJson(r.Body) + + userId := props["user_id"] + if len(userId) != 26 { + c.SetInvalidParam("adminResetMfa", "user_id") + return + } + + if err := DeactivateMfa(userId); err != nil { + c.Err = err + return + } + + c.LogAudit("") + + rdata := map[string]string{} + rdata["status"] = "ok" + w.Write([]byte(model.MapToJson(rdata))) +} + +func adminResetPassword(c *Context, w http.ResponseWriter, r *http.Request) { + props := model.MapFromJson(r.Body) + + userId := props["user_id"] + if len(userId) != 26 { + c.SetInvalidParam("adminResetPassword", "user_id") + return + } + + newPassword := props["new_password"] + if len(newPassword) < model.MIN_PASSWORD_LENGTH { + c.SetInvalidParam("adminResetPassword", "new_password") + return + } + + if err := ResetPassword(c, userId, newPassword); err != nil { + c.Err = err + return + } + + c.LogAudit("") + + rdata := map[string]string{} + rdata["status"] = "ok" + w.Write([]byte(model.MapToJson(rdata))) +} diff --git a/api/admin_test.go b/api/admin_test.go index 67bc1d38b..2edc151bd 100644 --- a/api/admin_test.go +++ b/api/admin_test.go @@ -7,33 +7,18 @@ import ( "github.com/mattermost/platform/model" "github.com/mattermost/platform/store" "github.com/mattermost/platform/utils" + "strings" "testing" ) func TestGetLogs(t *testing.T) { - Setup() + th := Setup().InitSystemAdmin().InitBasic() - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user.Id)) - - Client.LoginByEmail(team.Name, user.Email, "pwd") - - if _, err := Client.GetLogs(); err == nil { + if _, err := th.BasicClient.GetLogs(); err == nil { t.Fatal("Shouldn't have permissions") } - c := &Context{} - c.RequestId = model.NewId() - c.IpAddress = "cmd_line" - UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN) - - Client.LoginByEmail(team.Name, user.Email, "pwd") - - if logs, err := Client.GetLogs(); err != nil { + if logs, err := th.SystemAdminClient.GetLogs(); err != nil { t.Fatal(err) } else if len(logs.Data.([]string)) <= 0 { t.Fatal() @@ -41,29 +26,13 @@ func TestGetLogs(t *testing.T) { } func TestGetAllAudits(t *testing.T) { - Setup() + th := Setup().InitBasic().InitSystemAdmin() - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user.Id)) - - Client.LoginByEmail(team.Name, user.Email, "pwd") - - if _, err := Client.GetAllAudits(); err == nil { + if _, err := th.BasicClient.GetAllAudits(); err == nil { t.Fatal("Shouldn't have permissions") } - c := &Context{} - c.RequestId = model.NewId() - c.IpAddress = "cmd_line" - UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN) - - Client.LoginByEmail(team.Name, user.Email, "pwd") - - if audits, err := Client.GetAllAudits(); err != nil { + if audits, err := th.SystemAdminClient.GetAllAudits(); err != nil { t.Fatal(err) } else if len(audits.Data.(model.Audits)) <= 0 { t.Fatal() @@ -71,9 +40,9 @@ func TestGetAllAudits(t *testing.T) { } func TestGetClientProperties(t *testing.T) { - Setup() + th := Setup().InitBasic() - if result, err := Client.GetClientProperties(); err != nil { + if result, err := th.BasicClient.GetClientProperties(); err != nil { t.Fatal(err) } else { props := result.Data.(map[string]string) @@ -85,29 +54,13 @@ func TestGetClientProperties(t *testing.T) { } func TestGetConfig(t *testing.T) { - Setup() + th := Setup().InitBasic().InitSystemAdmin() - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user.Id)) - - Client.LoginByEmail(team.Name, user.Email, "pwd") - - if _, err := Client.GetConfig(); err == nil { + if _, err := th.BasicClient.GetConfig(); err == nil { t.Fatal("Shouldn't have permissions") } - c := &Context{} - c.RequestId = model.NewId() - c.IpAddress = "cmd_line" - UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN) - - Client.LoginByEmail(team.Name, user.Email, "pwd") - - if result, err := Client.GetConfig(); err != nil { + if result, err := th.SystemAdminClient.GetConfig(); err != nil { t.Fatal(err) } else { cfg := result.Data.(*model.Config) @@ -119,29 +72,15 @@ func TestGetConfig(t *testing.T) { } func TestSaveConfig(t *testing.T) { - Setup() + th := Setup().InitBasic().InitSystemAdmin() - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user.Id)) - - Client.LoginByEmail(team.Name, user.Email, "pwd") - - if _, err := Client.SaveConfig(utils.Cfg); err == nil { + if _, err := th.BasicClient.SaveConfig(utils.Cfg); err == nil { t.Fatal("Shouldn't have permissions") } - c := &Context{} - c.RequestId = model.NewId() - c.IpAddress = "cmd_line" - UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN) - - Client.LoginByEmail(team.Name, user.Email, "pwd") + *utils.Cfg.TeamSettings.EnableOpenServer = false - if result, err := Client.SaveConfig(utils.Cfg); err != nil { + if result, err := th.SystemAdminClient.SaveConfig(utils.Cfg); err != nil { t.Fatal(err) } else { cfg := result.Data.(*model.Config) @@ -150,66 +89,31 @@ func TestSaveConfig(t *testing.T) { t.Fatal() } } + + *utils.Cfg.TeamSettings.EnableOpenServer = true } func TestEmailTest(t *testing.T) { - Setup() + th := Setup().InitBasic().InitSystemAdmin() - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user.Id)) - - Client.LoginByEmail(team.Name, user.Email, "pwd") - - if _, err := Client.TestEmail(utils.Cfg); err == nil { + if _, err := th.BasicClient.TestEmail(utils.Cfg); err == nil { t.Fatal("Shouldn't have permissions") } - c := &Context{} - c.RequestId = model.NewId() - c.IpAddress = "cmd_line" - UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN) - - Client.LoginByEmail(team.Name, user.Email, "pwd") - - if _, err := Client.TestEmail(utils.Cfg); err != nil { + if _, err := th.SystemAdminClient.TestEmail(utils.Cfg); err != nil { t.Fatal(err) } } func TestGetTeamAnalyticsStandard(t *testing.T) { - Setup() - - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user.Id)) - - Client.LoginByEmail(team.Name, user.Email, "pwd") - - channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_PRIVATE, TeamId: team.Id} - channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) - - post1 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"} - post1 = Client.Must(Client.CreatePost(post1)).Data.(*model.Post) + th := Setup().InitBasic().InitSystemAdmin() + th.CreatePrivateChannel(th.BasicClient, th.BasicTeam) - if _, err := Client.GetTeamAnalytics(team.Id, "standard"); err == nil { + if _, err := th.BasicClient.GetTeamAnalytics(th.BasicTeam.Id, "standard"); err == nil { t.Fatal("Shouldn't have permissions") } - c := &Context{} - c.RequestId = model.NewId() - c.IpAddress = "cmd_line" - UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN) - - Client.LoginByEmail(team.Name, user.Email, "pwd") - - if result, err := Client.GetTeamAnalytics(team.Id, "standard"); err != nil { + if result, err := th.SystemAdminClient.GetTeamAnalytics(th.BasicTeam.Id, "standard"); err != nil { t.Fatal(err) } else { rows := result.Data.(model.AnalyticsRows) @@ -219,7 +123,7 @@ func TestGetTeamAnalyticsStandard(t *testing.T) { t.Fatal() } - if rows[0].Value != 2 { + if rows[0].Value != 3 { t.Log(rows.ToJson()) t.Fatal() } @@ -249,7 +153,7 @@ func TestGetTeamAnalyticsStandard(t *testing.T) { t.Fatal() } - if rows[3].Value != 1 { + if rows[3].Value != 2 { t.Log(rows.ToJson()) t.Fatal() } @@ -265,7 +169,7 @@ func TestGetTeamAnalyticsStandard(t *testing.T) { } } - if result, err := Client.GetSystemAnalytics("standard"); err != nil { + if result, err := th.SystemAdminClient.GetSystemAnalytics("standard"); err != nil { t.Fatal(err) } else { rows := result.Data.(model.AnalyticsRows) @@ -275,7 +179,7 @@ func TestGetTeamAnalyticsStandard(t *testing.T) { t.Fatal() } - if rows[0].Value < 2 { + if rows[0].Value < 3 { t.Log(rows.ToJson()) t.Fatal() } @@ -323,39 +227,17 @@ func TestGetTeamAnalyticsStandard(t *testing.T) { } func TestGetPostCount(t *testing.T) { - Setup() - - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user.Id)) - - Client.LoginByEmail(team.Name, user.Email, "pwd") - - channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_PRIVATE, TeamId: team.Id} - channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) - - post1 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"} - post1 = Client.Must(Client.CreatePost(post1)).Data.(*model.Post) + th := Setup().InitBasic().InitSystemAdmin() // manually update creation time, since it's always set to 0 upon saving and we only retrieve posts < today Srv.Store.(*store.SqlStore).GetMaster().Exec("UPDATE Posts SET CreateAt = :CreateAt WHERE ChannelId = :ChannelId", - map[string]interface{}{"ChannelId": channel1.Id, "CreateAt": utils.MillisFromTime(utils.Yesterday())}) + map[string]interface{}{"ChannelId": th.BasicChannel.Id, "CreateAt": utils.MillisFromTime(utils.Yesterday())}) - if _, err := Client.GetTeamAnalytics(team.Id, "post_counts_day"); err == nil { + if _, err := th.BasicClient.GetTeamAnalytics(th.BasicTeam.Id, "post_counts_day"); err == nil { t.Fatal("Shouldn't have permissions") } - c := &Context{} - c.RequestId = model.NewId() - c.IpAddress = "cmd_line" - UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN) - - Client.LoginByEmail(team.Name, user.Email, "pwd") - - if result, err := Client.GetTeamAnalytics(team.Id, "post_counts_day"); err != nil { + if result, err := th.SystemAdminClient.GetTeamAnalytics(th.BasicTeam.Id, "post_counts_day"); err != nil { t.Fatal(err) } else { rows := result.Data.(model.AnalyticsRows) @@ -368,39 +250,17 @@ func TestGetPostCount(t *testing.T) { } func TestUserCountsWithPostsByDay(t *testing.T) { - Setup() - - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user.Id)) - - Client.LoginByEmail(team.Name, user.Email, "pwd") - - channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_PRIVATE, TeamId: team.Id} - channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) - - post1 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"} - post1 = Client.Must(Client.CreatePost(post1)).Data.(*model.Post) + th := Setup().InitBasic().InitSystemAdmin() // manually update creation time, since it's always set to 0 upon saving and we only retrieve posts < today Srv.Store.(*store.SqlStore).GetMaster().Exec("UPDATE Posts SET CreateAt = :CreateAt WHERE ChannelId = :ChannelId", - map[string]interface{}{"ChannelId": channel1.Id, "CreateAt": utils.MillisFromTime(utils.Yesterday())}) + map[string]interface{}{"ChannelId": th.BasicChannel.Id, "CreateAt": utils.MillisFromTime(utils.Yesterday())}) - if _, err := Client.GetTeamAnalytics(team.Id, "user_counts_with_posts_day"); err == nil { + if _, err := th.BasicClient.GetTeamAnalytics(th.BasicTeam.Id, "user_counts_with_posts_day"); err == nil { t.Fatal("Shouldn't have permissions") } - c := &Context{} - c.RequestId = model.NewId() - c.IpAddress = "cmd_line" - UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN) - - Client.LoginByEmail(team.Name, user.Email, "pwd") - - if result, err := Client.GetTeamAnalytics(team.Id, "user_counts_with_posts_day"); err != nil { + if result, err := th.SystemAdminClient.GetTeamAnalytics(th.BasicTeam.Id, "user_counts_with_posts_day"); err != nil { t.Fatal(err) } else { rows := result.Data.(model.AnalyticsRows) @@ -413,38 +273,15 @@ func TestUserCountsWithPostsByDay(t *testing.T) { } func TestGetTeamAnalyticsExtra(t *testing.T) { - Setup() - - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user.Id)) - - Client.LoginByEmail(team.Name, user.Email, "pwd") - - channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_PRIVATE, TeamId: team.Id} - channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) - - post1 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"} - post1 = Client.Must(Client.CreatePost(post1)).Data.(*model.Post) + th := Setup().InitBasic().InitSystemAdmin() - post2 := &model.Post{ChannelId: channel1.Id, Message: "#test a" + model.NewId() + "a"} - post2 = Client.Must(Client.CreatePost(post2)).Data.(*model.Post) + th.CreatePost(th.BasicClient, th.BasicChannel) - if _, err := Client.GetTeamAnalytics("", "extra_counts"); err == nil { + if _, err := th.BasicClient.GetTeamAnalytics("", "extra_counts"); err == nil { t.Fatal("Shouldn't have permissions") } - c := &Context{} - c.RequestId = model.NewId() - c.IpAddress = "cmd_line" - UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN) - - Client.LoginByEmail(team.Name, user.Email, "pwd") - - if result, err := Client.GetTeamAnalytics(team.Id, "extra_counts"); err != nil { + if result, err := th.SystemAdminClient.GetTeamAnalytics(th.BasicTeam.Id, "extra_counts"); err != nil { t.Fatal(err) } else { rows := result.Data.(model.AnalyticsRows) @@ -464,7 +301,7 @@ func TestGetTeamAnalyticsExtra(t *testing.T) { t.Fatal() } - if rows[1].Value != 1 { + if rows[1].Value != 0 { t.Log(rows.ToJson()) t.Fatal() } @@ -510,7 +347,7 @@ func TestGetTeamAnalyticsExtra(t *testing.T) { } } - if result, err := Client.GetSystemAnalytics("extra_counts"); err != nil { + if result, err := th.SystemAdminClient.GetSystemAnalytics("extra_counts"); err != nil { t.Fatal(err) } else { rows := result.Data.(model.AnalyticsRows) @@ -525,11 +362,6 @@ func TestGetTeamAnalyticsExtra(t *testing.T) { t.Fatal() } - if rows[1].Value < 1 { - t.Log(rows.ToJson()) - t.Fatal() - } - if rows[2].Name != "incoming_webhook_count" { t.Log(rows.ToJson()) t.Fatal() @@ -551,3 +383,73 @@ func TestGetTeamAnalyticsExtra(t *testing.T) { } } } + +func TestAdminResetMfa(t *testing.T) { + th := Setup().InitBasic().InitSystemAdmin() + + if _, err := th.BasicClient.AdminResetMfa("12345678901234567890123456"); err == nil { + t.Fatal("should have failed - not an admin") + } + + if _, err := th.SystemAdminClient.AdminResetMfa(""); err == nil { + t.Fatal("should have failed - empty user id") + } + + if _, err := th.SystemAdminClient.AdminResetMfa("12345678901234567890123456"); err == nil { + t.Fatal("should have failed - bad user id") + } + + if _, err := th.SystemAdminClient.AdminResetMfa(th.BasicUser.Id); err == nil { + t.Fatal("should have failed - not licensed or configured") + } + + // need to add more test cases when enterprise bits can be loaded into tests +} + +func TestAdminResetPassword(t *testing.T) { + th := Setup().InitSystemAdmin() + Client := th.SystemAdminClient + team := th.SystemAdminTeam + + user := &model.User{Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} + user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) + LinkUserToTeam(user, team) + store.Must(Srv.Store.User().VerifyEmail(user.Id)) + + if _, err := Client.AdminResetPassword("", "newpwd"); err == nil { + t.Fatal("Should have errored - empty user id") + } + + if _, err := Client.AdminResetPassword("123", "newpwd"); err == nil { + t.Fatal("Should have errored - bad user id") + } + + if _, err := Client.AdminResetPassword("12345678901234567890123456", "newpwd"); err == nil { + t.Fatal("Should have errored - bad user id") + } + + if _, err := Client.AdminResetPassword("12345678901234567890123456", "newp"); err == nil { + t.Fatal("Should have errored - password too short") + } + + user2 := &model.User{Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", AuthData: "1", AuthService: "random"} + user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) + LinkUserToTeam(user2, team) + store.Must(Srv.Store.User().VerifyEmail(user2.Id)) + + if _, err := Client.AdminResetPassword(user2.Id, "newpwd"); err == nil { + t.Fatal("should have errored - SSO user can't reset password") + } + + if _, err := Client.AdminResetPassword(user.Id, "newpwd"); err != nil { + t.Fatal(err) + } + + Client.Logout() + Client.Must(Client.LoginById(user.Id, "newpwd")) + Client.SetTeamId(team.Id) + + if _, err := Client.AdminResetPassword(user.Id, "newpwd"); err == nil { + t.Fatal("Should have errored - not sytem admin") + } +} diff --git a/api/api.go b/api/api.go index 476047877..e9a95b125 100644 --- a/api/api.go +++ b/api/api.go @@ -6,6 +6,7 @@ package api import ( "net/http" + "github.com/gorilla/mux" "github.com/mattermost/platform/model" "github.com/mattermost/platform/utils" @@ -13,20 +14,71 @@ import ( _ "github.com/nicksnyder/go-i18n/i18n" ) +type Routes struct { + Root *mux.Router // '' + ApiRoot *mux.Router // 'api/v3' + + Users *mux.Router // 'api/v3/users' + NeedUser *mux.Router // 'api/v3/users/{user_id:[A-Za-z0-9]+}' + + Teams *mux.Router // 'api/v3/teams' + NeedTeam *mux.Router // 'api/v3/teams/{team_id:[A-Za-z0-9]+}' + + Channels *mux.Router // 'api/v3/teams/{team_id:[A-Za-z0-9]+}/channels' + NeedChannel *mux.Router // 'api/v3/teams/{team_id:[A-Za-z0-9]+}/channels/{channel_id:[A-Za-z0-9]+}' + + Posts *mux.Router // 'api/v3/teams/{team_id:[A-Za-z0-9]+}/channels/{channel_id:[A-Za-z0-9]+}/posts' + NeedPost *mux.Router // 'api/v3/teams/{team_id:[A-Za-z0-9]+}/channels/{channel_id:[A-Za-z0-9]+}/posts/{post_id:[A-Za-z0-9]+}' + + Commands *mux.Router // 'api/v3/teams/{team_id:[A-Za-z0-9]+}/commands' + Hooks *mux.Router // 'api/v3/teams/{team_id:[A-Za-z0-9]+}/hooks' + + Files *mux.Router // 'api/v3/teams/{team_id:[A-Za-z0-9]+}/files' + + OAuth *mux.Router // 'api/v3/oauth' + + Admin *mux.Router // 'api/v3/admin' + + Preferences *mux.Router // 'api/v3/preferences' + + License *mux.Router // 'api/v3/license' +} + +var BaseRoutes *Routes + func InitApi() { - r := Srv.Router.PathPrefix("/api/v1").Subrouter() - InitUser(r) - InitTeam(r) - InitChannel(r) - InitPost(r) - InitWebSocket(r) - InitFile(r) - InitCommand(r) - InitAdmin(r) - InitOAuth(r) - InitWebhook(r) - InitPreference(r) - InitLicense(r) + BaseRoutes = &Routes{} + BaseRoutes.Root = Srv.Router + BaseRoutes.ApiRoot = Srv.Router.PathPrefix(model.API_URL_SUFFIX).Subrouter() + BaseRoutes.Users = BaseRoutes.ApiRoot.PathPrefix("/users").Subrouter() + BaseRoutes.NeedUser = BaseRoutes.Users.PathPrefix("/{user_id:[A-Za-z0-9]+}").Subrouter() + BaseRoutes.Teams = BaseRoutes.ApiRoot.PathPrefix("/teams").Subrouter() + BaseRoutes.NeedTeam = BaseRoutes.Teams.PathPrefix("/{team_id:[A-Za-z0-9]+}").Subrouter() + BaseRoutes.Channels = BaseRoutes.NeedTeam.PathPrefix("/channels").Subrouter() + BaseRoutes.NeedChannel = BaseRoutes.Channels.PathPrefix("/{channel_id:[A-Za-z0-9]+}").Subrouter() + BaseRoutes.Posts = BaseRoutes.NeedChannel.PathPrefix("/posts").Subrouter() + BaseRoutes.NeedPost = BaseRoutes.Posts.PathPrefix("/{post_id:[A-Za-z0-9]+}").Subrouter() + BaseRoutes.Commands = BaseRoutes.NeedTeam.PathPrefix("/commands").Subrouter() + BaseRoutes.Files = BaseRoutes.NeedTeam.PathPrefix("/files").Subrouter() + BaseRoutes.Hooks = BaseRoutes.NeedTeam.PathPrefix("/hooks").Subrouter() + BaseRoutes.OAuth = BaseRoutes.ApiRoot.PathPrefix("/oauth").Subrouter() + BaseRoutes.Admin = BaseRoutes.ApiRoot.PathPrefix("/admin").Subrouter() + BaseRoutes.Preferences = BaseRoutes.ApiRoot.PathPrefix("/preferences").Subrouter() + BaseRoutes.License = BaseRoutes.ApiRoot.PathPrefix("/license").Subrouter() + + InitUser() + InitTeam() + InitChannel() + InitPost() + InitWebSocket() + InitFile() + InitCommand() + InitAdmin() + InitOAuth() + InitWebhook() + InitPreference() + InitLicense() + // 404 on any api route before web.go has a chance to serve it Srv.Router.Handle("/api/{anything:.*}", http.HandlerFunc(Handle404)) diff --git a/api/api_test.go b/api/api_test.go deleted file mode 100644 index 94691ab4b..000000000 --- a/api/api_test.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -package api - -import ( - "github.com/mattermost/platform/model" - "github.com/mattermost/platform/store" - "github.com/mattermost/platform/utils" -) - -var Client *model.Client - -func Setup() { - if Srv == nil { - utils.LoadConfig("config.json") - utils.InitTranslations() - utils.Cfg.TeamSettings.MaxUsersPerTeam = 50 - NewServer() - StartServer() - InitApi() - Client = model.NewClient("http://localhost" + utils.Cfg.ServiceSettings.ListenAddress) - - Srv.Store.MarkSystemRanUnitTests() - } -} - -func SetupBenchmark() (*model.Team, *model.User, *model.Channel) { - Setup() - - team := &model.Team{DisplayName: "Benchmark Team", Name: "z-z-" + model.NewId() + "a", Email: "benchmark@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Mr. Benchmarker", Password: "pwd"} - user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user.Id)) - Client.LoginByEmail(team.Name, user.Email, "pwd") - channel := &model.Channel{DisplayName: "Benchmark Channel", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} - channel = Client.Must(Client.CreateChannel(channel)).Data.(*model.Channel) - - return team, user, channel -} - -func TearDown() { - if Srv != nil { - StopServer() - } -} diff --git a/api/apitestlib.go b/api/apitestlib.go new file mode 100644 index 000000000..d82dc30be --- /dev/null +++ b/api/apitestlib.go @@ -0,0 +1,223 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package api + +import ( + "time" + + "github.com/mattermost/platform/model" + "github.com/mattermost/platform/store" + "github.com/mattermost/platform/utils" + + l4g "github.com/alecthomas/log4go" +) + +type TestHelper struct { + BasicClient *model.Client + BasicTeam *model.Team + BasicUser *model.User + BasicUser2 *model.User + BasicChannel *model.Channel + BasicPost *model.Post + + SystemAdminClient *model.Client + SystemAdminTeam *model.Team + SystemAdminUser *model.User + SystemAdminChannel *model.Channel +} + +func SetupEnterprise(platformDir string) *TestHelper { + if Srv == nil { + utils.LoadConfig(platformDir + "/config/config.json") + utils.InitTranslationsWithDir(platformDir + "/i18n") + utils.Cfg.TeamSettings.MaxUsersPerTeam = 50 + utils.DisableDebugLogForTest() + utils.License.Features.SetDefaults() + NewServer() + StartServer() + utils.InitHTMLWithDir(platformDir + "/templates") + InitApi() + utils.EnableDebugLogForTest() + Srv.Store.MarkSystemRanUnitTests() + + *utils.Cfg.TeamSettings.EnableOpenServer = true + } + + return &TestHelper{} +} + +func Setup() *TestHelper { + if Srv == nil { + utils.LoadConfig("config.json") + utils.InitTranslations() + utils.Cfg.TeamSettings.MaxUsersPerTeam = 50 + utils.DisableDebugLogForTest() + NewServer() + StartServer() + InitApi() + utils.EnableDebugLogForTest() + Srv.Store.MarkSystemRanUnitTests() + + *utils.Cfg.TeamSettings.EnableOpenServer = true + } + + return &TestHelper{} +} + +func (me *TestHelper) InitBasic() *TestHelper { + me.BasicClient = me.CreateClient() + me.BasicTeam = me.CreateTeam(me.BasicClient) + me.BasicUser = me.CreateUser(me.BasicClient) + LinkUserToTeam(me.BasicUser, me.BasicTeam) + me.BasicUser2 = me.CreateUser(me.BasicClient) + LinkUserToTeam(me.BasicUser2, me.BasicTeam) + me.BasicClient.SetTeamId(me.BasicTeam.Id) + me.LoginBasic() + me.BasicChannel = me.CreateChannel(me.BasicClient, me.BasicTeam) + me.BasicPost = me.CreatePost(me.BasicClient, me.BasicChannel) + + return me +} + +func (me *TestHelper) InitSystemAdmin() *TestHelper { + me.SystemAdminClient = me.CreateClient() + me.SystemAdminTeam = me.CreateTeam(me.SystemAdminClient) + me.SystemAdminUser = me.CreateUser(me.SystemAdminClient) + LinkUserToTeam(me.SystemAdminUser, me.SystemAdminTeam) + me.SystemAdminClient.SetTeamId(me.SystemAdminTeam.Id) + c := &Context{} + c.RequestId = model.NewId() + c.IpAddress = "cmd_line" + UpdateRoles(c, me.SystemAdminUser, model.ROLE_SYSTEM_ADMIN) + me.SystemAdminUser.Password = "Password1" + me.LoginSystemAdmin() + me.SystemAdminChannel = me.CreateChannel(me.SystemAdminClient, me.SystemAdminTeam) + + return me +} + +func (me *TestHelper) CreateClient() *model.Client { + return model.NewClient("http://localhost" + utils.Cfg.ServiceSettings.ListenAddress) +} + +func (me *TestHelper) CreateTeam(client *model.Client) *model.Team { + id := model.NewId() + team := &model.Team{ + DisplayName: "dn_" + id, + Name: "name" + id, + Email: "success+" + id + "@simulator.amazonses.com", + Type: model.TEAM_OPEN, + } + + utils.DisableDebugLogForTest() + r := client.Must(client.CreateTeam(team)).Data.(*model.Team) + utils.EnableDebugLogForTest() + return r +} + +func (me *TestHelper) CreateUser(client *model.Client) *model.User { + id := model.NewId() + + user := &model.User{ + Email: "success+" + id + "@simulator.amazonses.com", + Username: "un_" + id, + Nickname: "nn_" + id, + Password: "Password1", + } + + utils.DisableDebugLogForTest() + ruser := client.Must(client.CreateUser(user, "")).Data.(*model.User) + ruser.Password = "Password1" + store.Must(Srv.Store.User().VerifyEmail(ruser.Id)) + utils.EnableDebugLogForTest() + return ruser +} + +func LinkUserToTeam(user *model.User, team *model.Team) { + utils.DisableDebugLogForTest() + + err := JoinUserToTeam(team, user) + if err != nil { + l4g.Error(err.Error()) + l4g.Close() + time.Sleep(time.Second) + panic(err) + } + + utils.EnableDebugLogForTest() +} + +func UpdateUserToTeamAdmin(user *model.User, team *model.Team) { + utils.DisableDebugLogForTest() + + tm := &model.TeamMember{TeamId: team.Id, UserId: user.Id, Roles: model.ROLE_TEAM_ADMIN} + if tmr := <-Srv.Store.Team().UpdateMember(tm); tmr.Err != nil { + l4g.Error(tmr.Err.Error()) + l4g.Close() + time.Sleep(time.Second) + panic(tmr.Err) + } +} + +func (me *TestHelper) CreateChannel(client *model.Client, team *model.Team) *model.Channel { + return me.createChannel(client, team, model.CHANNEL_OPEN) +} + +func (me *TestHelper) CreatePrivateChannel(client *model.Client, team *model.Team) *model.Channel { + return me.createChannel(client, team, model.CHANNEL_PRIVATE) +} + +func (me *TestHelper) createChannel(client *model.Client, team *model.Team, channelType string) *model.Channel { + id := model.NewId() + + channel := &model.Channel{ + DisplayName: "dn_" + id, + Name: "name_" + id, + Type: channelType, + TeamId: team.Id, + } + + utils.DisableDebugLogForTest() + r := client.Must(client.CreateChannel(channel)).Data.(*model.Channel) + utils.EnableDebugLogForTest() + return r +} + +func (me *TestHelper) CreatePost(client *model.Client, channel *model.Channel) *model.Post { + id := model.NewId() + + post := &model.Post{ + ChannelId: channel.Id, + Message: "message_" + id, + } + + utils.DisableDebugLogForTest() + r := client.Must(client.CreatePost(post)).Data.(*model.Post) + utils.EnableDebugLogForTest() + return r +} + +func (me *TestHelper) LoginBasic() { + utils.DisableDebugLogForTest() + me.BasicClient.Must(me.BasicClient.LoginByEmail(me.BasicTeam.Name, me.BasicUser.Email, me.BasicUser.Password)) + utils.EnableDebugLogForTest() +} + +func (me *TestHelper) LoginBasic2() { + utils.DisableDebugLogForTest() + me.BasicClient.Must(me.BasicClient.LoginByEmail(me.BasicTeam.Name, me.BasicUser2.Email, me.BasicUser2.Password)) + utils.EnableDebugLogForTest() +} + +func (me *TestHelper) LoginSystemAdmin() { + utils.DisableDebugLogForTest() + me.SystemAdminClient.Must(me.SystemAdminClient.LoginByEmail(me.SystemAdminTeam.Name, me.SystemAdminUser.Email, me.SystemAdminUser.Password)) + utils.EnableDebugLogForTest() +} + +func TearDown() { + if Srv != nil { + StopServer() + } +} diff --git a/api/authentication.go b/api/authentication.go new file mode 100644 index 000000000..bab83a720 --- /dev/null +++ b/api/authentication.go @@ -0,0 +1,99 @@ +// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package api + +import ( + "github.com/mattermost/platform/einterfaces" + "github.com/mattermost/platform/model" + "github.com/mattermost/platform/utils" +) + +func checkPasswordAndAllCriteria(user *model.User, password string, mfaToken string) *model.AppError { + if err := checkUserPassword(user, password); err != nil { + return err + } + + if err := checkUserAdditionalAuthenticationCriteria(user, mfaToken); err != nil { + return err + } + + return nil +} + +func checkUserPassword(user *model.User, password string) *model.AppError { + if !model.ComparePassword(user.Password, password) { + if result := <-Srv.Store.User().UpdateFailedPasswordAttempts(user.Id, user.FailedAttempts+1); result.Err != nil { + return result.Err + } + + return model.NewLocAppError("checkUserPassword", "api.user.check_user_password.invalid.app_error", nil, "user_id="+user.Id) + } else { + if result := <-Srv.Store.User().UpdateFailedPasswordAttempts(user.Id, 0); result.Err != nil { + return result.Err + } + + return nil + } +} + +func checkUserAdditionalAuthenticationCriteria(user *model.User, mfaToken string) *model.AppError { + if err := checkUserMfa(user, mfaToken); err != nil { + return err + } + + if err := checkEmailVerified(user); err != nil { + return err + } + + if err := checkUserNotDisabled(user); err != nil { + return err + } + + if err := checkUserLoginAttempts(user); err != nil { + return err + } + + return nil +} + +func checkUserMfa(user *model.User, token string) *model.AppError { + if !user.MfaActive || !utils.IsLicensed || !*utils.License.Features.MFA || !*utils.Cfg.ServiceSettings.EnableMultifactorAuthentication { + return nil + } + + mfaInterface := einterfaces.GetMfaInterface() + if mfaInterface == nil { + return model.NewLocAppError("checkUserMfa", "api.user.check_user_mfa.not_available.app_error", nil, "") + } + + if ok, err := mfaInterface.ValidateToken(user.MfaSecret, token); err != nil { + return err + } else if !ok { + return model.NewLocAppError("checkUserMfa", "api.user.check_user_mfa.bad_code.app_error", nil, "") + } + + return nil +} + +func checkUserLoginAttempts(user *model.User) *model.AppError { + if user.FailedAttempts >= utils.Cfg.ServiceSettings.MaximumLoginAttempts { + return model.NewLocAppError("checkUserLoginAttempts", "api.user.check_user_login_attempts.too_many.app_error", nil, "user_id="+user.Id) + } + + return nil +} + +func checkEmailVerified(user *model.User) *model.AppError { + if !user.EmailVerified && utils.Cfg.EmailSettings.RequireEmailVerification { + return model.NewLocAppError("Login", "api.user.login.not_verified.app_error", nil, "user_id="+user.Id) + } + return nil +} + +func checkUserNotDisabled(user *model.User) *model.AppError { + if user.DeleteAt > 0 { + return model.NewLocAppError("Login", "api.user.login.inactive.app_error", nil, "user_id="+user.Id) + } + return nil +} diff --git a/api/auto_channels.go b/api/auto_channels.go index ab1fe6ed3..1d0f0e7d9 100644 --- a/api/auto_channels.go +++ b/api/auto_channels.go @@ -10,7 +10,7 @@ import ( type AutoChannelCreator struct { client *model.Client - teamID string + team *model.Team Fuzzy bool DisplayNameLen utils.Range DisplayNameCharset string @@ -19,10 +19,10 @@ type AutoChannelCreator struct { ChannelType string } -func NewAutoChannelCreator(client *model.Client, teamID string) *AutoChannelCreator { +func NewAutoChannelCreator(client *model.Client, team *model.Team) *AutoChannelCreator { return &AutoChannelCreator{ client: client, - teamID: teamID, + team: team, Fuzzy: false, DisplayNameLen: CHANNEL_DISPLAY_NAME_LEN, DisplayNameCharset: utils.ALPHANUMERIC, @@ -42,13 +42,17 @@ func (cfg *AutoChannelCreator) createRandomChannel() (*model.Channel, bool) { name := utils.RandomName(cfg.NameLen, cfg.NameCharset) channel := &model.Channel{ - TeamId: cfg.teamID, + TeamId: cfg.team.Id, DisplayName: displayName, Name: name, Type: cfg.ChannelType} + println(cfg.client.GetTeamRoute()) result, err := cfg.client.CreateChannel(channel) if err != nil { + err.Translate(utils.T) + println(err.Error()) + println(err.DetailedError) return nil, false } return result.Data.(*model.Channel), true diff --git a/api/auto_environment.go b/api/auto_environment.go index 68186ec6c..270b43936 100644 --- a/api/auto_environment.go +++ b/api/auto_environment.go @@ -28,14 +28,15 @@ func CreateTestEnvironmentWithTeams(client *model.Client, rangeTeams utils.Range environment := TestEnvironment{teams, make([]TeamEnvironment, len(teams))} for i, team := range teams { - userCreator := NewAutoUserCreator(client, team.Id) + userCreator := NewAutoUserCreator(client, team) userCreator.Fuzzy = fuzzy randomUser, err := userCreator.createRandomUser() if err != true { return TestEnvironment{}, false } client.LoginById(randomUser.Id, USER_PASSWORD) - teamEnvironment, err := CreateTestEnvironmentInTeam(client, team.Id, rangeChannels, rangeUsers, rangePosts, fuzzy) + client.SetTeamId(team.Id) + teamEnvironment, err := CreateTestEnvironmentInTeam(client, team, rangeChannels, rangeUsers, rangePosts, fuzzy) if err != true { return TestEnvironment{}, false } @@ -45,7 +46,7 @@ func CreateTestEnvironmentWithTeams(client *model.Client, rangeTeams utils.Range return environment, true } -func CreateTestEnvironmentInTeam(client *model.Client, teamID string, rangeChannels utils.Range, rangeUsers utils.Range, rangePosts utils.Range, fuzzy bool) (TeamEnvironment, bool) { +func CreateTestEnvironmentInTeam(client *model.Client, team *model.Team, rangeChannels utils.Range, rangeUsers utils.Range, rangePosts utils.Range, fuzzy bool) (TeamEnvironment, bool) { rand.Seed(time.Now().UTC().UnixNano()) // We need to create at least one user @@ -53,7 +54,7 @@ func CreateTestEnvironmentInTeam(client *model.Client, teamID string, rangeChann rangeUsers.Begin = 1 } - userCreator := NewAutoUserCreator(client, teamID) + userCreator := NewAutoUserCreator(client, team) userCreator.Fuzzy = fuzzy users, err := userCreator.CreateTestUsers(rangeUsers) if err != true { @@ -64,7 +65,7 @@ func CreateTestEnvironmentInTeam(client *model.Client, teamID string, rangeChann usernames[i] = user.Username } - channelCreator := NewAutoChannelCreator(client, teamID) + channelCreator := NewAutoChannelCreator(client, team) channelCreator.Fuzzy = fuzzy channels, err := channelCreator.CreateTestChannels(rangeChannels) @@ -79,6 +80,7 @@ func CreateTestEnvironmentInTeam(client *model.Client, teamID string, rangeChann if err != true { return TeamEnvironment{}, false } + numPosts := utils.RandIntFromRange(rangePosts) numImages := utils.RandIntFromRange(rangePosts) / 4 for j := 0; j < numPosts; j++ { diff --git a/api/auto_posts.go b/api/auto_posts.go index b64217c55..2e26e513b 100644 --- a/api/auto_posts.go +++ b/api/auto_posts.go @@ -74,7 +74,7 @@ func (cfg *AutoPostCreator) UploadTestFile() ([]string, bool) { return nil, false } - resp, appErr := cfg.client.UploadFile("/files/upload", body.Bytes(), writer.FormDataContentType()) + resp, appErr := cfg.client.UploadPostAttachment(body.Bytes(), writer.FormDataContentType()) if appErr != nil { return nil, false } diff --git a/api/auto_teams.go b/api/auto_teams.go index 082415d32..b2e1ace85 100644 --- a/api/auto_teams.go +++ b/api/auto_teams.go @@ -42,11 +42,11 @@ func (cfg *AutoTeamCreator) createRandomTeam() (*model.Team, bool) { var teamDisplayName string var teamName string if cfg.Fuzzy { - teamEmail = utils.FuzzEmail() + teamEmail = "success+" + model.NewId() + "simulator.amazonses.com" teamDisplayName = utils.FuzzName() teamName = utils.FuzzName() } else { - teamEmail = utils.RandomEmail(cfg.EmailLength, cfg.EmailCharset) + teamEmail = "success+" + model.NewId() + "simulator.amazonses.com" teamDisplayName = utils.RandomName(cfg.NameLength, cfg.NameCharset) teamName = utils.RandomName(cfg.NameLength, cfg.NameCharset) + model.NewId() } diff --git a/api/auto_users.go b/api/auto_users.go index d1e3d494e..a23b76246 100644 --- a/api/auto_users.go +++ b/api/auto_users.go @@ -7,11 +7,13 @@ import ( "github.com/mattermost/platform/model" "github.com/mattermost/platform/store" "github.com/mattermost/platform/utils" + + l4g "github.com/alecthomas/log4go" ) type AutoUserCreator struct { client *model.Client - teamID string + team *model.Team EmailLength utils.Range EmailCharset string NameLength utils.Range @@ -19,10 +21,10 @@ type AutoUserCreator struct { Fuzzy bool } -func NewAutoUserCreator(client *model.Client, teamID string) *AutoUserCreator { +func NewAutoUserCreator(client *model.Client, team *model.Team) *AutoUserCreator { return &AutoUserCreator{ client: client, - teamID: teamID, + team: team, EmailLength: USER_EMAIL_LEN, EmailCharset: utils.LOWERCASE, NameLength: USER_NAME_LEN, @@ -33,7 +35,7 @@ func NewAutoUserCreator(client *model.Client, teamID string) *AutoUserCreator { // Basic test team and user so you always know one func CreateBasicUser(client *model.Client) *model.AppError { - result, _ := client.FindTeamByName(BTEST_TEAM_NAME, true) + result, _ := client.FindTeamByName(BTEST_TEAM_NAME) if result.Data.(bool) == false { newteam := &model.Team{DisplayName: BTEST_TEAM_DISPLAY_NAME, Name: BTEST_TEAM_NAME, Email: BTEST_TEAM_EMAIL, Type: BTEST_TEAM_TYPE} result, err := client.CreateTeam(newteam) @@ -41,12 +43,14 @@ func CreateBasicUser(client *model.Client) *model.AppError { return err } basicteam := result.Data.(*model.Team) - newuser := &model.User{TeamId: basicteam.Id, Email: BTEST_USER_EMAIL, Nickname: BTEST_USER_NAME, Password: BTEST_USER_PASSWORD} + newuser := &model.User{Email: BTEST_USER_EMAIL, Nickname: BTEST_USER_NAME, Password: BTEST_USER_PASSWORD} result, err = client.CreateUser(newuser, "") if err != nil { return err } - store.Must(Srv.Store.User().VerifyEmail(result.Data.(*model.User).Id)) + ruser := result.Data.(*model.User) + store.Must(Srv.Store.User().VerifyEmail(ruser.Id)) + store.Must(Srv.Store.Team().SaveMember(&model.TeamMember{TeamId: basicteam.Id, UserId: ruser.Id})) } return nil } @@ -55,25 +59,30 @@ func (cfg *AutoUserCreator) createRandomUser() (*model.User, bool) { var userEmail string var userName string if cfg.Fuzzy { - userEmail = utils.RandString(FUZZ_USER_EMAIL_PREFIX_LEN, utils.LOWERCASE) + "-" + utils.FuzzEmail() + userEmail = "success+" + model.NewId() + "simulator.amazonses.com" userName = utils.FuzzName() } else { - userEmail = utils.RandomEmail(cfg.EmailLength, cfg.EmailCharset) + userEmail = "success+" + model.NewId() + "simulator.amazonses.com" userName = utils.RandomName(cfg.NameLength, cfg.NameCharset) } user := &model.User{ - TeamId: cfg.teamID, Email: userEmail, Nickname: userName, Password: USER_PASSWORD} - result, err := cfg.client.CreateUser(user, "") + result, err := cfg.client.CreateUserWithInvite(user, "", "", cfg.team.InviteId) if err != nil { + err.Translate(utils.T) + l4g.Error(err.Error()) return nil, false } + + ruser := result.Data.(*model.User) + // We need to cheat to verify the user's email - store.Must(Srv.Store.User().VerifyEmail(result.Data.(*model.User).Id)) + store.Must(Srv.Store.User().VerifyEmail(ruser.Id)) + return result.Data.(*model.User), true } diff --git a/api/channel.go b/api/channel.go index e97e08fc0..871477824 100644 --- a/api/channel.go +++ b/api/channel.go @@ -18,29 +18,28 @@ const ( defaultExtraMemberLimit = 100 ) -func InitChannel(r *mux.Router) { +func InitChannel() { l4g.Debug(utils.T("api.channel.init.debug")) - sr := r.PathPrefix("/channels").Subrouter() - sr.Handle("/", ApiUserRequiredActivity(getChannels, false)).Methods("GET") - sr.Handle("/more", ApiUserRequired(getMoreChannels)).Methods("GET") - sr.Handle("/counts", ApiUserRequiredActivity(getChannelCounts, false)).Methods("GET") - sr.Handle("/create", ApiUserRequired(createChannel)).Methods("POST") - sr.Handle("/create_direct", ApiUserRequired(createDirectChannel)).Methods("POST") - sr.Handle("/update", ApiUserRequired(updateChannel)).Methods("POST") - sr.Handle("/update_header", ApiUserRequired(updateChannelHeader)).Methods("POST") - sr.Handle("/update_purpose", ApiUserRequired(updateChannelPurpose)).Methods("POST") - sr.Handle("/update_notify_props", ApiUserRequired(updateNotifyProps)).Methods("POST") - sr.Handle("/{id:[A-Za-z0-9]+}/", ApiUserRequiredActivity(getChannel, false)).Methods("GET") - sr.Handle("/{id:[A-Za-z0-9]+}/extra_info", ApiUserRequired(getChannelExtraInfo)).Methods("GET") - sr.Handle("/{id:[A-Za-z0-9]+}/extra_info/{member_limit:-?[0-9]+}", ApiUserRequired(getChannelExtraInfo)).Methods("GET") - sr.Handle("/{id:[A-Za-z0-9]+}/join", ApiUserRequired(join)).Methods("POST") - sr.Handle("/{id:[A-Za-z0-9]+}/leave", ApiUserRequired(leave)).Methods("POST") - sr.Handle("/{id:[A-Za-z0-9]+}/delete", ApiUserRequired(deleteChannel)).Methods("POST") - sr.Handle("/{id:[A-Za-z0-9]+}/add", ApiUserRequired(addMember)).Methods("POST") - sr.Handle("/{id:[A-Za-z0-9]+}/remove", ApiUserRequired(removeMember)).Methods("POST") - sr.Handle("/{id:[A-Za-z0-9]+}/update_last_viewed_at", ApiUserRequired(updateLastViewedAt)).Methods("POST") - + BaseRoutes.Channels.Handle("/", ApiUserRequiredActivity(getChannels, false)).Methods("GET") + BaseRoutes.Channels.Handle("/more", ApiUserRequired(getMoreChannels)).Methods("GET") + BaseRoutes.Channels.Handle("/counts", ApiUserRequiredActivity(getChannelCounts, false)).Methods("GET") + BaseRoutes.Channels.Handle("/create", ApiUserRequired(createChannel)).Methods("POST") + BaseRoutes.Channels.Handle("/create_direct", ApiUserRequired(createDirectChannel)).Methods("POST") + BaseRoutes.Channels.Handle("/update", ApiUserRequired(updateChannel)).Methods("POST") + BaseRoutes.Channels.Handle("/update_header", ApiUserRequired(updateChannelHeader)).Methods("POST") + BaseRoutes.Channels.Handle("/update_purpose", ApiUserRequired(updateChannelPurpose)).Methods("POST") + BaseRoutes.Channels.Handle("/update_notify_props", ApiUserRequired(updateNotifyProps)).Methods("POST") + + BaseRoutes.NeedChannel.Handle("/", ApiUserRequiredActivity(getChannel, false)).Methods("GET") + BaseRoutes.NeedChannel.Handle("/extra_info", ApiUserRequired(getChannelExtraInfo)).Methods("GET") + BaseRoutes.NeedChannel.Handle("/extra_info/{member_limit:-?[0-9]+}", ApiUserRequired(getChannelExtraInfo)).Methods("GET") + BaseRoutes.NeedChannel.Handle("/join", ApiUserRequired(join)).Methods("POST") + BaseRoutes.NeedChannel.Handle("/leave", ApiUserRequired(leave)).Methods("POST") + BaseRoutes.NeedChannel.Handle("/delete", ApiUserRequired(deleteChannel)).Methods("POST") + BaseRoutes.NeedChannel.Handle("/add", ApiUserRequired(addMember)).Methods("POST") + BaseRoutes.NeedChannel.Handle("/remove", ApiUserRequired(removeMember)).Methods("POST") + BaseRoutes.NeedChannel.Handle("/update_last_viewed_at", ApiUserRequired(updateLastViewedAt)).Methods("POST") } func createChannel(c *Context, w http.ResponseWriter, r *http.Request) { @@ -52,6 +51,10 @@ func createChannel(c *Context, w http.ResponseWriter, r *http.Request) { return } + if len(channel.TeamId) == 0 { + channel.TeamId = c.TeamId + } + if !c.HasPermissionsToTeam(channel.TeamId, "createChannel") { return } @@ -107,10 +110,6 @@ func createDirectChannel(c *Context, w http.ResponseWriter, r *http.Request) { return } - if !c.HasPermissionsToTeam(c.Session.TeamId, "createDirectChannel") { - return - } - if sc, err := CreateDirectChannel(c, userId); err != nil { c.Err = err return @@ -131,7 +130,7 @@ func CreateDirectChannel(c *Context, otherUserId string) (*model.Channel, *model channel.DisplayName = "" channel.Name = model.GetDMNameFromIds(otherUserId, c.Session.UserId) - channel.TeamId = c.Session.TeamId + channel.TeamId = c.TeamId channel.Header = "" channel.Type = model.CHANNEL_DIRECT @@ -214,7 +213,7 @@ func updateChannel(c *Context, w http.ResponseWriter, r *http.Request) { if oldChannel.Name == model.DEFAULT_CHANNEL { if (len(channel.Name) > 0 && channel.Name != oldChannel.Name) || (len(channel.Type) > 0 && channel.Type != oldChannel.Type) { c.Err = model.NewLocAppError("updateChannel", "api.channel.update_channel.tried.app_error", map[string]interface{}{"Channel": model.DEFAULT_CHANNEL}, "") - c.Err.StatusCode = http.StatusForbidden + c.Err.StatusCode = http.StatusBadRequest return } } @@ -367,7 +366,7 @@ func getChannels(c *Context, w http.ResponseWriter, r *http.Request) { // user is already in the team - if result := <-Srv.Store.Channel().GetChannels(c.Session.TeamId, c.Session.UserId); result.Err != nil { + if result := <-Srv.Store.Channel().GetChannels(c.TeamId, c.Session.UserId); result.Err != nil { if result.Err.Id == "store.sql_channel.get_channels.not_found.app_error" { // lets make sure the user is valid if result := <-Srv.Store.User().Get(c.Session.UserId); result.Err != nil { @@ -392,7 +391,7 @@ func getMoreChannels(c *Context, w http.ResponseWriter, r *http.Request) { // user is already in the team - if result := <-Srv.Store.Channel().GetMoreChannels(c.Session.TeamId, c.Session.UserId); result.Err != nil { + if result := <-Srv.Store.Channel().GetMoreChannels(c.TeamId, c.Session.UserId); result.Err != nil { c.Err = result.Err return } else if HandleEtag(result.Data.(*model.ChannelList).Etag(), w, r) { @@ -408,7 +407,7 @@ func getChannelCounts(c *Context, w http.ResponseWriter, r *http.Request) { // user is already in the team - if result := <-Srv.Store.Channel().GetChannelCounts(c.Session.TeamId, c.Session.UserId); result.Err != nil { + if result := <-Srv.Store.Channel().GetChannelCounts(c.TeamId, c.Session.UserId); result.Err != nil { c.Err = model.NewLocAppError("getChannelCounts", "api.channel.get_channel_counts.app_error", nil, result.Err.Message) return } else if HandleEtag(result.Data.(*model.ChannelCounts).Etag(), w, r) { @@ -423,7 +422,7 @@ func getChannelCounts(c *Context, w http.ResponseWriter, r *http.Request) { func join(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) - channelId := params["id"] + channelId := params["channel_id"] JoinChannel(c, channelId, "") @@ -498,7 +497,7 @@ func AddUserToChannel(user *model.User, channel *model.Channel) (*model.ChannelM } go func() { - UpdateChannelAccessCache(channel.TeamId, user.Id, channel.Id) + InvalidateCacheForUser(user.Id) message := model.NewMessage(channel.TeamId, channel.Id, user.Id, model.ACTION_USER_ADDED) PublishAndForget(message) @@ -507,12 +506,12 @@ func AddUserToChannel(user *model.User, channel *model.Channel) (*model.ChannelM return newMember, nil } -func JoinDefaultChannels(user *model.User, channelRole string) *model.AppError { +func JoinDefaultChannels(teamId string, user *model.User, channelRole string) *model.AppError { // We don't call JoinChannel here since c.Session is not populated on user creation var err *model.AppError = nil - if result := <-Srv.Store.Channel().GetByName(user.TeamId, "town-square"); result.Err != nil { + if result := <-Srv.Store.Channel().GetByName(teamId, "town-square"); result.Err != nil { err = result.Err } else { cm := &model.ChannelMember{ChannelId: result.Data.(*model.Channel).Id, UserId: user.Id, @@ -523,7 +522,7 @@ func JoinDefaultChannels(user *model.User, channelRole string) *model.AppError { } } - if result := <-Srv.Store.Channel().GetByName(user.TeamId, "off-topic"); result.Err != nil { + if result := <-Srv.Store.Channel().GetByName(teamId, "off-topic"); result.Err != nil { err = result.Err } else { cm := &model.ChannelMember{ChannelId: result.Data.(*model.Channel).Id, UserId: user.Id, @@ -540,7 +539,7 @@ func JoinDefaultChannels(user *model.User, channelRole string) *model.AppError { func leave(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) - id := params["id"] + id := params["channel_id"] sc := Srv.Store.Channel().Get(id) uc := Srv.Store.User().Get(c.Session.UserId) @@ -561,13 +560,13 @@ func leave(c *Context, w http.ResponseWriter, r *http.Request) { if channel.Type == model.CHANNEL_DIRECT { c.Err = model.NewLocAppError("leave", "api.channel.leave.direct.app_error", nil, "") - c.Err.StatusCode = http.StatusForbidden + c.Err.StatusCode = http.StatusBadRequest return } if channel.Name == model.DEFAULT_CHANNEL { c.Err = model.NewLocAppError("leave", "api.channel.leave.default.app_error", map[string]interface{}{"Channel": model.DEFAULT_CHANNEL}, "") - c.Err.StatusCode = http.StatusForbidden + c.Err.StatusCode = http.StatusBadRequest return } @@ -589,7 +588,7 @@ func leave(c *Context, w http.ResponseWriter, r *http.Request) { func deleteChannel(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) - id := params["id"] + id := params["channel_id"] sc := Srv.Store.Channel().Get(id) scm := Srv.Store.Channel().GetMember(id, c.Session.UserId) @@ -637,7 +636,7 @@ func deleteChannel(c *Context, w http.ResponseWriter, r *http.Request) { if channel.Name == model.DEFAULT_CHANNEL { c.Err = model.NewLocAppError("deleteChannel", "api.channel.delete_channel.cannot.app_error", map[string]interface{}{"Channel": model.DEFAULT_CHANNEL}, "") - c.Err.StatusCode = http.StatusForbidden + c.Err.StatusCode = http.StatusBadRequest return } @@ -682,7 +681,7 @@ func deleteChannel(c *Context, w http.ResponseWriter, r *http.Request) { func updateLastViewedAt(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) - id := params["id"] + id := params["channel_id"] Srv.Store.Channel().UpdateLastViewedAt(id, c.Session.UserId) @@ -695,7 +694,7 @@ func updateLastViewedAt(c *Context, w http.ResponseWriter, r *http.Request) { Srv.Store.Preference().Save(&model.Preferences{preference}) - message := model.NewMessage(c.Session.TeamId, id, c.Session.UserId, model.ACTION_CHANNEL_VIEWED) + message := model.NewMessage(c.TeamId, id, c.Session.UserId, model.ACTION_CHANNEL_VIEWED) message.Add("channel_id", id) PublishAndForget(message) @@ -707,9 +706,9 @@ func updateLastViewedAt(c *Context, w http.ResponseWriter, r *http.Request) { func getChannel(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) - id := params["id"] + id := params["channel_id"] - //pchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, id, c.Session.UserId) + //pchan := Srv.Store.Channel().CheckPermissionsTo(c.TeamId, id, c.Session.UserId) cchan := Srv.Store.Channel().Get(id) cmchan := Srv.Store.Channel().GetMember(id, c.Session.UserId) @@ -737,7 +736,7 @@ func getChannel(c *Context, w http.ResponseWriter, r *http.Request) { func getChannelExtraInfo(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) - id := params["id"] + id := params["channel_id"] var memberLimit int if memberLimitString, ok := params["member_limit"]; !ok { @@ -781,7 +780,7 @@ func getChannelExtraInfo(c *Context, w http.ResponseWriter, r *http.Request) { extraMembers := ecmresult.Data.([]model.ExtraMember) memberCount := ccmresult.Data.(int64) - if !c.HasPermissionsToTeam(channel.TeamId, "getChannelExtraInfo") { + if len(channel.TeamId) > 0 && !c.HasPermissionsToTeam(channel.TeamId, "getChannelExtraInfo") { return } @@ -803,7 +802,7 @@ func getChannelExtraInfo(c *Context, w http.ResponseWriter, r *http.Request) { func addMember(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) - id := params["id"] + id := params["channel_id"] data := model.MapFromJson(r.Body) userId := data["user_id"] @@ -813,7 +812,7 @@ func addMember(c *Context, w http.ResponseWriter, r *http.Request) { return } - cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, id, c.Session.UserId) + cchan := Srv.Store.Channel().CheckPermissionsTo(c.TeamId, id, c.Session.UserId) sc := Srv.Store.Channel().Get(id) ouc := Srv.Store.User().Get(c.Session.UserId) nuc := Srv.Store.User().Get(userId) @@ -857,7 +856,7 @@ func addMember(c *Context, w http.ResponseWriter, r *http.Request) { func removeMember(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) - channelId := params["id"] + channelId := params["channel_id"] data := model.MapFromJson(r.Body) userIdToRemove := data["user_id"] @@ -914,7 +913,7 @@ func RemoveUserFromChannel(userIdToRemove string, removerUserId string, channel return cmresult.Err } - UpdateChannelAccessCacheAndForget(channel.TeamId, userIdToRemove, channel.Id) + InvalidateCacheForUser(userIdToRemove) message := model.NewMessage(channel.TeamId, channel.Id, userIdToRemove, model.ACTION_USER_REMOVED) message.Add("remover_id", removerUserId) @@ -938,7 +937,7 @@ func updateNotifyProps(c *Context, w http.ResponseWriter, r *http.Request) { return } - cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, channelId, c.Session.UserId) + cchan := Srv.Store.Channel().CheckPermissionsTo(c.TeamId, channelId, c.Session.UserId) if !c.HasPermissionsToUser(userId, "updateNotifyLevel") { return diff --git a/api/channel_benchmark_test.go b/api/channel_benchmark_test.go index 09c734cc2..3e7c2882c 100644 --- a/api/channel_benchmark_test.go +++ b/api/channel_benchmark_test.go @@ -12,61 +12,47 @@ import ( const ( NUM_CHANNELS = 140 + NUM_USERS = 40 ) func BenchmarkCreateChannel(b *testing.B) { - var ( - NUM_CHANNELS_RANGE = utils.Range{NUM_CHANNELS, NUM_CHANNELS} - ) - team, _, _ := SetupBenchmark() + th := Setup().InitBasic() - channelCreator := NewAutoChannelCreator(Client, team.Id) + channelCreator := NewAutoChannelCreator(th.BasicClient, th.BasicTeam) - // Benchmark Start b.ResetTimer() for i := 0; i < b.N; i++ { - channelCreator.CreateTestChannels(NUM_CHANNELS_RANGE) + channelCreator.CreateTestChannels(utils.Range{NUM_CHANNELS, NUM_CHANNELS}) } } func BenchmarkCreateDirectChannel(b *testing.B) { - var ( - NUM_CHANNELS_RANGE = utils.Range{NUM_CHANNELS, NUM_CHANNELS} - ) - team, _, _ := SetupBenchmark() + th := Setup().InitBasic() - userCreator := NewAutoUserCreator(Client, team.Id) - users, err := userCreator.CreateTestUsers(NUM_CHANNELS_RANGE) + userCreator := NewAutoUserCreator(th.BasicClient, th.BasicTeam) + users, err := userCreator.CreateTestUsers(utils.Range{NUM_USERS, NUM_USERS}) if err == false { b.Fatal("Could not create users") } - data := make([]map[string]string, len(users)) - - for i := range data { - newmap := map[string]string{ - "user_id": users[i].Id, - } - data[i] = newmap - } - // Benchmark Start b.ResetTimer() for i := 0; i < b.N; i++ { - for j := 0; j < NUM_CHANNELS; j++ { - Client.CreateDirectChannel(data[j]) + for j := 0; j < NUM_USERS; j++ { + th.BasicClient.CreateDirectChannel(users[j].Id) } } } func BenchmarkUpdateChannel(b *testing.B) { + th := Setup().InitBasic() + var ( NUM_CHANNELS_RANGE = utils.Range{NUM_CHANNELS, NUM_CHANNELS} CHANNEL_HEADER_LEN = 50 ) - team, _, _ := SetupBenchmark() - channelCreator := NewAutoChannelCreator(Client, team.Id) + channelCreator := NewAutoChannelCreator(th.BasicClient, th.BasicTeam) channels, valid := channelCreator.CreateTestChannels(NUM_CHANNELS_RANGE) if valid == false { b.Fatal("Unable to create test channels") @@ -80,7 +66,7 @@ func BenchmarkUpdateChannel(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { for j := range channels { - if _, err := Client.UpdateChannel(channels[j]); err != nil { + if _, err := th.BasicClient.UpdateChannel(channels[j]); err != nil { b.Fatal(err) } } @@ -88,12 +74,13 @@ func BenchmarkUpdateChannel(b *testing.B) { } func BenchmarkGetChannels(b *testing.B) { + th := Setup().InitBasic() + var ( NUM_CHANNELS_RANGE = utils.Range{NUM_CHANNELS, NUM_CHANNELS} ) - team, _, _ := SetupBenchmark() - channelCreator := NewAutoChannelCreator(Client, team.Id) + channelCreator := NewAutoChannelCreator(th.BasicClient, th.BasicTeam) _, valid := channelCreator.CreateTestChannels(NUM_CHANNELS_RANGE) if valid == false { b.Fatal("Unable to create test channels") @@ -102,17 +89,18 @@ func BenchmarkGetChannels(b *testing.B) { // Benchmark Start b.ResetTimer() for i := 0; i < b.N; i++ { - Client.Must(Client.GetChannels("")) + th.BasicClient.Must(th.BasicClient.GetChannels("")) } } func BenchmarkGetMoreChannels(b *testing.B) { + th := Setup().InitBasic() + var ( NUM_CHANNELS_RANGE = utils.Range{NUM_CHANNELS, NUM_CHANNELS} ) - team, _, _ := SetupBenchmark() - channelCreator := NewAutoChannelCreator(Client, team.Id) + channelCreator := NewAutoChannelCreator(th.BasicClient, th.BasicTeam) _, valid := channelCreator.CreateTestChannels(NUM_CHANNELS_RANGE) if valid == false { b.Fatal("Unable to create test channels") @@ -121,44 +109,47 @@ func BenchmarkGetMoreChannels(b *testing.B) { // Benchmark Start b.ResetTimer() for i := 0; i < b.N; i++ { - Client.Must(Client.GetMoreChannels("")) + th.BasicClient.Must(th.BasicClient.GetMoreChannels("")) } } func BenchmarkJoinChannel(b *testing.B) { + th := Setup().InitBasic() + var ( NUM_CHANNELS_RANGE = utils.Range{NUM_CHANNELS, NUM_CHANNELS} ) - team, _, _ := SetupBenchmark() - channelCreator := NewAutoChannelCreator(Client, team.Id) + channelCreator := NewAutoChannelCreator(th.BasicClient, th.BasicTeam) channels, valid := channelCreator.CreateTestChannels(NUM_CHANNELS_RANGE) if valid == false { b.Fatal("Unable to create test channels") } // Secondary test user to join channels created by primary test user - user := &model.User{TeamId: team.Id, Email: "success+" + model.NewId() + "@simulator.amazonses.com", Nickname: "That Guy", Password: "pwd"} - user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) + user := &model.User{Email: "success+" + model.NewId() + "@simulator.amazonses.com", Nickname: "That Guy", Password: "pwd"} + user = th.BasicClient.Must(th.BasicClient.CreateUser(user, "")).Data.(*model.User) + LinkUserToTeam(user, th.BasicTeam) store.Must(Srv.Store.User().VerifyEmail(user.Id)) - Client.LoginByEmail(team.Name, user.Email, "pwd") + th.BasicClient.LoginByEmail(th.BasicTeam.Name, user.Email, "pwd") // Benchmark Start b.ResetTimer() for i := 0; i < b.N; i++ { for j := range channels { - Client.Must(Client.JoinChannel(channels[j].Id)) + th.BasicClient.Must(th.BasicClient.JoinChannel(channels[j].Id)) } } } func BenchmarkDeleteChannel(b *testing.B) { + th := Setup().InitBasic() + var ( NUM_CHANNELS_RANGE = utils.Range{NUM_CHANNELS, NUM_CHANNELS} ) - team, _, _ := SetupBenchmark() - channelCreator := NewAutoChannelCreator(Client, team.Id) + channelCreator := NewAutoChannelCreator(th.BasicClient, th.BasicTeam) channels, valid := channelCreator.CreateTestChannels(NUM_CHANNELS_RANGE) if valid == false { b.Fatal("Unable to create test channels") @@ -168,18 +159,19 @@ func BenchmarkDeleteChannel(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { for j := range channels { - Client.Must(Client.DeleteChannel(channels[j].Id)) + th.BasicClient.Must(th.BasicClient.DeleteChannel(channels[j].Id)) } } } func BenchmarkGetChannelExtraInfo(b *testing.B) { + th := Setup().InitBasic() + var ( NUM_CHANNELS_RANGE = utils.Range{NUM_CHANNELS, NUM_CHANNELS} ) - team, _, _ := SetupBenchmark() - channelCreator := NewAutoChannelCreator(Client, team.Id) + channelCreator := NewAutoChannelCreator(th.BasicClient, th.BasicTeam) channels, valid := channelCreator.CreateTestChannels(NUM_CHANNELS_RANGE) if valid == false { b.Fatal("Unable to create test channels") @@ -189,22 +181,23 @@ func BenchmarkGetChannelExtraInfo(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { for j := range channels { - Client.Must(Client.GetChannelExtraInfo(channels[j].Id, -1, "")) + th.BasicClient.Must(th.BasicClient.GetChannelExtraInfo(channels[j].Id, -1, "")) } } } func BenchmarkAddChannelMember(b *testing.B) { + th := Setup().InitBasic() + var ( NUM_USERS = 100 NUM_USERS_RANGE = utils.Range{NUM_USERS, NUM_USERS} ) - team, _, _ := SetupBenchmark() - channel := &model.Channel{DisplayName: "Test Channel", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} - channel = Client.Must(Client.CreateChannel(channel)).Data.(*model.Channel) + channel := &model.Channel{DisplayName: "Test Channel", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: th.BasicTeam.Id} + channel = th.BasicClient.Must(th.BasicClient.CreateChannel(channel)).Data.(*model.Channel) - userCreator := NewAutoUserCreator(Client, team.Id) + userCreator := NewAutoUserCreator(th.BasicClient, th.BasicTeam) users, valid := userCreator.CreateTestUsers(NUM_USERS_RANGE) if valid == false { b.Fatal("Unable to create test users") @@ -214,7 +207,7 @@ func BenchmarkAddChannelMember(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { for j := range users { - if _, err := Client.AddChannelMember(channel.Id, users[j].Id); err != nil { + if _, err := th.BasicClient.AddChannelMember(channel.Id, users[j].Id); err != nil { b.Fatal(err) } } @@ -223,23 +216,24 @@ func BenchmarkAddChannelMember(b *testing.B) { // Is this benchmark failing? Raise your file ulimit! 2048 worked for me. func BenchmarkRemoveChannelMember(b *testing.B) { + th := Setup().InitBasic() + var ( NUM_USERS = 140 NUM_USERS_RANGE = utils.Range{NUM_USERS, NUM_USERS} ) - team, _, _ := SetupBenchmark() - channel := &model.Channel{DisplayName: "Test Channel", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} - channel = Client.Must(Client.CreateChannel(channel)).Data.(*model.Channel) + channel := &model.Channel{DisplayName: "Test Channel", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: th.BasicTeam.Id} + channel = th.BasicClient.Must(th.BasicClient.CreateChannel(channel)).Data.(*model.Channel) - userCreator := NewAutoUserCreator(Client, team.Id) + userCreator := NewAutoUserCreator(th.BasicClient, th.BasicTeam) users, valid := userCreator.CreateTestUsers(NUM_USERS_RANGE) if valid == false { b.Fatal("Unable to create test users") } for i := range users { - if _, err := Client.AddChannelMember(channel.Id, users[i].Id); err != nil { + if _, err := th.BasicClient.AddChannelMember(channel.Id, users[i].Id); err != nil { b.Fatal(err) } } @@ -248,7 +242,7 @@ func BenchmarkRemoveChannelMember(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { for j := range users { - if _, err := Client.RemoveChannelMember(channel.Id, users[j].Id); err != nil { + if _, err := th.BasicClient.RemoveChannelMember(channel.Id, users[j].Id); err != nil { b.Fatal(err) } } @@ -256,12 +250,13 @@ func BenchmarkRemoveChannelMember(b *testing.B) { } func BenchmarkUpdateNotifyProps(b *testing.B) { + th := Setup().InitBasic() + var ( NUM_CHANNELS_RANGE = utils.Range{NUM_CHANNELS, NUM_CHANNELS} ) - team, user, _ := SetupBenchmark() - channelCreator := NewAutoChannelCreator(Client, team.Id) + channelCreator := NewAutoChannelCreator(th.BasicClient, th.BasicTeam) channels, valid := channelCreator.CreateTestChannels(NUM_CHANNELS_RANGE) if valid == false { b.Fatal("Unable to create test channels") @@ -272,7 +267,7 @@ func BenchmarkUpdateNotifyProps(b *testing.B) { for i := range data { newmap := map[string]string{ "channel_id": channels[i].Id, - "user_id": user.Id, + "user_id": th.BasicUser.Id, "desktop": model.CHANNEL_NOTIFY_MENTION, "mark_unread": model.CHANNEL_MARK_UNREAD_MENTION, } @@ -283,7 +278,7 @@ func BenchmarkUpdateNotifyProps(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { for j := range channels { - Client.Must(Client.UpdateNotifyProps(data[j])) + th.BasicClient.Must(th.BasicClient.UpdateNotifyProps(data[j])) } } } diff --git a/api/channel_test.go b/api/channel_test.go index c3015f924..23dd77698 100644 --- a/api/channel_test.go +++ b/api/channel_test.go @@ -14,19 +14,13 @@ import ( ) func TestCreateChannel(t *testing.T) { - Setup() - - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - team2 := &model.Team{DisplayName: "Name Team 2", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team2 = Client.Must(Client.CreateTeam(team2)).Data.(*model.Team) - - user := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user.Id)) - - Client.LoginByEmail(team.Name, user.Email, "pwd") + th := Setup().InitBasic() + Client := th.BasicClient + team := th.BasicTeam + Client.Must(Client.Logout()) + team2 := th.CreateTeam(th.BasicClient) + th.LoginBasic() + th.BasicClient.SetTeamId(team.Id) channel := model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} rchannel, err := Client.CreateChannel(&channel) @@ -63,7 +57,7 @@ func TestCreateChannel(t *testing.T) { } } - if _, err := Client.DoApiPost("/channels/create", "garbage"); err == nil { + if _, err := Client.DoApiPost(Client.GetTeamRoute()+"/channels/create", "garbage"); err == nil { t.Fatal("should have been an error") } @@ -94,25 +88,12 @@ func TestCreateChannel(t *testing.T) { } func TestCreateDirectChannel(t *testing.T) { - Setup() - - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user.Id)) - - user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user2.Id)) - - Client.LoginByEmail(team.Name, user.Email, "pwd") - - data := make(map[string]string) - data["user_id"] = user2.Id + th := Setup().InitBasic() + Client := th.BasicClient + user := th.BasicUser + user2 := th.BasicUser2 - rchannel, err := Client.CreateDirectChannel(data) + rchannel, err := Client.CreateDirectChannel(th.BasicUser2.Id) if err != nil { t.Fatal(err) } @@ -132,47 +113,31 @@ func TestCreateDirectChannel(t *testing.T) { t.Fatal("channel type was not direct") } - if _, err := Client.CreateDirectChannel(data); err == nil { + if _, err := Client.CreateDirectChannel(th.BasicUser2.Id); err == nil { t.Fatal("channel already exists and should have failed") } - data["user_id"] = "junk" - if _, err := Client.CreateDirectChannel(data); err == nil { + if _, err := Client.CreateDirectChannel("junk"); err == nil { t.Fatal("should have failed with bad user id") } - data["user_id"] = "12345678901234567890123456" - if _, err := Client.CreateDirectChannel(data); err == nil { + if _, err := Client.CreateDirectChannel("12345678901234567890123456"); err == nil { t.Fatal("should have failed with non-existent user") } } func TestUpdateChannel(t *testing.T) { - Setup() - - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - userTeamAdmin := &model.User{TeamId: team.Id, Email: team.Email, Nickname: "Corey Hulen", Password: "pwd"} - userTeamAdmin = Client.Must(Client.CreateUser(userTeamAdmin, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(userTeamAdmin.Id)) - - userChannelAdmin := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - userChannelAdmin = Client.Must(Client.CreateUser(userChannelAdmin, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(userChannelAdmin.Id)) - - userStd := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - userStd = Client.Must(Client.CreateUser(userStd, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(userStd.Id)) - userStd.Roles = "" - - Client.LoginByEmail(team.Name, userChannelAdmin.Email, "pwd") + th := Setup().InitBasic() + Client := th.BasicClient + team := th.BasicTeam + user := th.BasicUser + user2 := th.CreateUser(th.BasicClient) channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) - Client.AddChannelMember(channel1.Id, userTeamAdmin.Id) + Client.AddChannelMember(channel1.Id, user.Id) header := "a" + model.NewId() + "a" purpose := "a" + model.NewId() + "a" @@ -191,25 +156,6 @@ func TestUpdateChannel(t *testing.T) { t.Fatal("Channel admin failed to skip displayName") } - Client.LoginByEmail(team.Name, userTeamAdmin.Email, "pwd") - - header = "b" + model.NewId() + "b" - purpose = "b" + model.NewId() + "b" - upChannel1 = &model.Channel{Id: channel1.Id, Header: header, Purpose: purpose} - upChannel1 = Client.Must(Client.UpdateChannel(upChannel1)).Data.(*model.Channel) - - if upChannel1.Header != header { - t.Fatal("Team admin failed to update header") - } - - if upChannel1.Purpose != purpose { - t.Fatal("Team admin failed to update purpose") - } - - if upChannel1.DisplayName != channel1.DisplayName { - t.Fatal("Team admin failed to skip displayName") - } - rget := Client.Must(Client.GetChannels("")) data := rget.Data.(*model.ChannelList) for _, c := range data.Channels { @@ -223,7 +169,7 @@ func TestUpdateChannel(t *testing.T) { } } - Client.LoginByEmail(team.Name, userStd.Email, "pwd") + Client.LoginByEmail(team.Name, user2.Email, user2.Password) if _, err := Client.UpdateChannel(upChannel1); err == nil { t.Fatal("Standard User should have failed to update") @@ -231,16 +177,9 @@ func TestUpdateChannel(t *testing.T) { } func TestUpdateChannelHeader(t *testing.T) { - Setup() - - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user.Id)) - - Client.LoginByEmail(team.Name, user.Email, "pwd") + th := Setup().InitBasic() + Client := th.BasicClient + team := th.BasicTeam channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) @@ -276,11 +215,7 @@ func TestUpdateChannelHeader(t *testing.T) { t.Fatal("should have errored on bad channel header") } - user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user2.Id)) - - Client.LoginByEmail(team.Name, user2.Email, "pwd") + th.LoginBasic2() data["channel_id"] = channel1.Id data["channel_header"] = "new header" @@ -290,16 +225,9 @@ func TestUpdateChannelHeader(t *testing.T) { } func TestUpdateChannelPurpose(t *testing.T) { - Setup() - - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user.Id)) - - Client.LoginByEmail(team.Name, user.Email, "pwd") + th := Setup().InitBasic() + Client := th.BasicClient + team := th.BasicTeam channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) @@ -335,11 +263,7 @@ func TestUpdateChannelPurpose(t *testing.T) { t.Fatal("should have errored on bad channel purpose") } - user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user2.Id)) - - Client.LoginByEmail(team.Name, user2.Email, "pwd") + th.LoginBasic2() data["channel_id"] = channel1.Id data["channel_purpose"] = "new purpose" @@ -349,16 +273,9 @@ func TestUpdateChannelPurpose(t *testing.T) { } func TestGetChannel(t *testing.T) { - Setup() - - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user.Id)) - - Client.LoginByEmail(team.Name, user.Email, "pwd") + th := Setup().InitBasic() + Client := th.BasicClient + team := th.BasicTeam channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) @@ -412,16 +329,9 @@ func TestGetChannel(t *testing.T) { } func TestGetMoreChannel(t *testing.T) { - Setup() - - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - - Client.LoginByEmail(team.Name, user1.Email, "pwd") + th := Setup().InitBasic() + Client := th.BasicClient + team := th.BasicTeam channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) @@ -429,11 +339,7 @@ func TestGetMoreChannel(t *testing.T) { channel2 := &model.Channel{DisplayName: "B Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel) - user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user2.Id)) - - Client.LoginByEmail(team.Name, user2.Email, "pwd") + th.LoginBasic2() rget := Client.Must(Client.GetMoreChannels("")) data := rget.Data.(*model.ChannelList) @@ -456,16 +362,9 @@ func TestGetMoreChannel(t *testing.T) { } func TestGetChannelCounts(t *testing.T) { - Setup() - - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user.Id)) - - Client.LoginByEmail(team.Name, user.Email, "pwd") + th := Setup().InitBasic() + Client := th.BasicClient + team := th.BasicTeam channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) @@ -478,11 +377,11 @@ func TestGetChannelCounts(t *testing.T) { } else { counts := result.Data.(*model.ChannelCounts) - if len(counts.Counts) != 4 { + if len(counts.Counts) != 5 { t.Fatal("wrong number of channel counts") } - if len(counts.UpdateTimes) != 4 { + if len(counts.UpdateTimes) != 5 { t.Fatal("wrong number of channel update times") } @@ -497,16 +396,9 @@ func TestGetChannelCounts(t *testing.T) { } func TestJoinChannel(t *testing.T) { - Setup() - - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - - Client.LoginByEmail(team.Name, user1.Email, "pwd") + th := Setup().InitBasic() + Client := th.BasicClient + team := th.BasicTeam channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) @@ -514,11 +406,7 @@ func TestJoinChannel(t *testing.T) { channel3 := &model.Channel{DisplayName: "B Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_PRIVATE, TeamId: team.Id} channel3 = Client.Must(Client.CreateChannel(channel3)).Data.(*model.Channel) - user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user2.Id)) - - Client.LoginByEmail(team.Name, user2.Email, "pwd") + th.LoginBasic2() Client.Must(Client.JoinChannel(channel1.Id)) @@ -526,13 +414,10 @@ func TestJoinChannel(t *testing.T) { t.Fatal("shouldn't be able to join secret group") } - data := make(map[string]string) - data["user_id"] = user1.Id - rchannel := Client.Must(Client.CreateDirectChannel(data)).Data.(*model.Channel) - - user3 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user3 = Client.Must(Client.CreateUser(user3, "")).Data.(*model.User) + rchannel := Client.Must(Client.CreateDirectChannel(th.BasicUser.Id)).Data.(*model.Channel) + user3 := th.CreateUser(th.BasicClient) + LinkUserToTeam(user3, team) Client.LoginByEmail(team.Name, user3.Email, "pwd") if _, err := Client.JoinChannel(rchannel.Id); err == nil { @@ -541,16 +426,9 @@ func TestJoinChannel(t *testing.T) { } func TestLeaveChannel(t *testing.T) { - Setup() - - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - - Client.LoginByEmail(team.Name, user1.Email, "pwd") + th := Setup().InitBasic() + Client := th.BasicClient + team := th.BasicTeam channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) @@ -558,20 +436,14 @@ func TestLeaveChannel(t *testing.T) { channel3 := &model.Channel{DisplayName: "B Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_PRIVATE, TeamId: team.Id} channel3 = Client.Must(Client.CreateChannel(channel3)).Data.(*model.Channel) - user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user2.Id)) - - Client.LoginByEmail(team.Name, user2.Email, "pwd") + th.LoginBasic2() Client.Must(Client.JoinChannel(channel1.Id)) // No error if you leave a channel you cannot see Client.Must(Client.LeaveChannel(channel3.Id)) - data := make(map[string]string) - data["user_id"] = user1.Id - rchannel := Client.Must(Client.CreateDirectChannel(data)).Data.(*model.Channel) + rchannel := Client.Must(Client.CreateDirectChannel(th.BasicUser.Id)).Data.(*model.Channel) if _, err := Client.LeaveChannel(rchannel.Id); err == nil { t.Fatal("should have errored, cannot leave direct channel") @@ -590,20 +462,12 @@ func TestLeaveChannel(t *testing.T) { } func TestDeleteChannel(t *testing.T) { - Setup() - - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - userTeamAdmin := &model.User{TeamId: team.Id, Email: team.Email, Nickname: "Corey Hulen", Password: "pwd"} - userTeamAdmin = Client.Must(Client.CreateUser(userTeamAdmin, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(userTeamAdmin.Id)) - - userChannelAdmin := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - userChannelAdmin = Client.Must(Client.CreateUser(userChannelAdmin, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(userChannelAdmin.Id)) + th := Setup().InitBasic() + Client := th.BasicClient + team := th.BasicTeam + userTeamAdmin := th.BasicUser - Client.LoginByEmail(team.Name, userChannelAdmin.Email, "pwd") + th.LoginBasic2() channelMadeByCA := &model.Channel{DisplayName: "C Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channelMadeByCA = Client.Must(Client.CreateChannel(channelMadeByCA)).Data.(*model.Channel) @@ -631,11 +495,9 @@ func TestDeleteChannel(t *testing.T) { t.Fatal("should have failed to post to deleted channel") } - userStd := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - userStd = Client.Must(Client.CreateUser(userStd, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(userStd.Id)) - - Client.LoginByEmail(team.Name, userStd.Email, "pwd") + userStd := th.CreateUser(th.BasicClient) + LinkUserToTeam(userStd, team) + Client.LoginByEmail(team.Name, userStd.Email, userStd.Password) if _, err := Client.JoinChannel(channel1.Id); err == nil { t.Fatal("should have failed to join deleted channel") @@ -660,16 +522,9 @@ func TestDeleteChannel(t *testing.T) { } func TestGetChannelExtraInfo(t *testing.T) { - Setup() - - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user.Id)) - - Client.LoginByEmail(team.Name, user.Email, "pwd") + th := Setup().InitBasic() + Client := th.BasicClient + team := th.BasicTeam channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) @@ -701,8 +556,10 @@ func TestGetChannelExtraInfo(t *testing.T) { Client2 := model.NewClient("http://localhost" + utils.Cfg.ServiceSettings.ListenAddress) - user2 := &model.User{TeamId: team.Id, Email: "success+" + model.NewId() + "@simulator.amazonses.com", Nickname: "Tester 2", Password: "pwd"} + user2 := &model.User{Email: "success+" + model.NewId() + "@simulator.amazonses.com", Nickname: "Tester 2", Password: "pwd"} user2 = Client2.Must(Client2.CreateUser(user2, "")).Data.(*model.User) + LinkUserToTeam(user2, team) + Client2.SetTeamId(team.Id) store.Must(Srv.Store.User().VerifyEmail(user2.Id)) Client2.LoginByEmail(team.Name, user2.Email, "pwd") @@ -784,24 +641,14 @@ func TestGetChannelExtraInfo(t *testing.T) { } func TestAddChannelMember(t *testing.T) { - Setup() - - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - - Client.LoginByEmail(team.Name, user1.Email, "pwd") + th := Setup().InitBasic() + Client := th.BasicClient + team := th.BasicTeam + user2 := th.BasicUser2 channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) - user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user2.Id)) - if _, err := Client.AddChannelMember(channel1.Id, user2.Id); err != nil { t.Fatal(err) } @@ -825,13 +672,13 @@ func TestAddChannelMember(t *testing.T) { channel2 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel) - Client.LoginByEmail(team.Name, user2.Email, "pwd") + th.LoginBasic2() if _, err := Client.AddChannelMember(channel2.Id, user2.Id); err == nil { t.Fatal("Should have errored, user not in channel") } - Client.LoginByEmail(team.Name, user1.Email, "pwd") + th.LoginBasic() Client.Must(Client.DeleteChannel(channel2.Id)) @@ -842,34 +689,24 @@ func TestAddChannelMember(t *testing.T) { } func TestRemoveChannelMember(t *testing.T) { - Setup() - - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - userTeamAdmin := &model.User{TeamId: team.Id, Email: team.Email, Nickname: "Corey Hulen", Password: "pwd"} - userTeamAdmin = Client.Must(Client.CreateUser(userTeamAdmin, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(userTeamAdmin.Id)) - - userChannelAdmin := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - userChannelAdmin = Client.Must(Client.CreateUser(userChannelAdmin, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(userChannelAdmin.Id)) - - Client.LoginByEmail(team.Name, userChannelAdmin.Email, "pwd") + th := Setup().InitBasic() + Client := th.BasicClient + team := th.BasicTeam + user2 := th.BasicUser2 + UpdateUserToTeamAdmin(user2, team) channelMadeByCA := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channelMadeByCA = Client.Must(Client.CreateChannel(channelMadeByCA)).Data.(*model.Channel) - Client.Must(Client.AddChannelMember(channelMadeByCA.Id, userTeamAdmin.Id)) + Client.Must(Client.AddChannelMember(channelMadeByCA.Id, user2.Id)) - Client.LoginByEmail(team.Name, userTeamAdmin.Email, "pwd") + th.LoginBasic2() channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) - userStd := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - userStd = Client.Must(Client.CreateUser(userStd, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(userStd.Id)) + userStd := th.CreateUser(th.BasicClient) + LinkUserToTeam(userStd, team) Client.Must(Client.AddChannelMember(channel1.Id, userStd.Id)) @@ -894,13 +731,13 @@ func TestRemoveChannelMember(t *testing.T) { channel2 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel) - Client.LoginByEmail(team.Name, userStd.Email, "pwd") + Client.LoginByEmail(team.Name, userStd.Email, userStd.Password) if _, err := Client.RemoveChannelMember(channel2.Id, userStd.Id); err == nil { t.Fatal("Should have errored, user not channel admin") } - Client.LoginByEmail(team.Name, userTeamAdmin.Email, "pwd") + th.LoginBasic2() Client.Must(Client.AddChannelMember(channel2.Id, userStd.Id)) Client.Must(Client.DeleteChannel(channel2.Id)) @@ -912,16 +749,11 @@ func TestRemoveChannelMember(t *testing.T) { } func TestUpdateNotifyProps(t *testing.T) { - Setup() - - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user.Id)) - - Client.LoginByEmail(team.Name, user.Email, "pwd") + th := Setup().InitBasic() + Client := th.BasicClient + team := th.BasicTeam + user := th.BasicUser + user2 := th.BasicUser2 channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) @@ -1019,10 +851,7 @@ func TestUpdateNotifyProps(t *testing.T) { t.Fatal("Should have errored - bad mark unread level") } - user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) - - Client.LoginByEmail(team.Name, user2.Email, "pwd") + th.LoginBasic2() data["channel_id"] = channel1.Id data["user_id"] = user2.Id @@ -1034,16 +863,9 @@ func TestUpdateNotifyProps(t *testing.T) { } func TestFuzzyChannel(t *testing.T) { - Setup() - - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user.Id)) - - Client.LoginByEmail(team.Name, user.Email, "pwd") + th := Setup().InitBasic() + Client := th.BasicClient + team := th.BasicTeam // Strings that should pass as acceptable channel names var fuzzyStringsPass = []string{ diff --git a/api/command.go b/api/command.go index 99fd05d7a..72249a48c 100644 --- a/api/command.go +++ b/api/command.go @@ -12,7 +12,7 @@ import ( "strings" l4g "github.com/alecthomas/log4go" - "github.com/gorilla/mux" + "github.com/mattermost/platform/model" "github.com/mattermost/platform/utils" ) @@ -38,23 +38,21 @@ func GetCommandProvider(name string) CommandProvider { return nil } -func InitCommand(r *mux.Router) { +func InitCommand() { l4g.Debug(utils.T("api.command.init.debug")) - sr := r.PathPrefix("/commands").Subrouter() - - sr.Handle("/execute", ApiUserRequired(executeCommand)).Methods("POST") - sr.Handle("/list", ApiUserRequired(listCommands)).Methods("GET") + BaseRoutes.Commands.Handle("/execute", ApiUserRequired(executeCommand)).Methods("POST") + BaseRoutes.Commands.Handle("/list", ApiUserRequired(listCommands)).Methods("GET") - sr.Handle("/create", ApiUserRequired(createCommand)).Methods("POST") - sr.Handle("/list_team_commands", ApiUserRequired(listTeamCommands)).Methods("GET") - sr.Handle("/regen_token", ApiUserRequired(regenCommandToken)).Methods("POST") - sr.Handle("/delete", ApiUserRequired(deleteCommand)).Methods("POST") + BaseRoutes.Commands.Handle("/create", ApiUserRequired(createCommand)).Methods("POST") + BaseRoutes.Commands.Handle("/list_team_commands", ApiUserRequired(listTeamCommands)).Methods("GET") + BaseRoutes.Commands.Handle("/regen_token", ApiUserRequired(regenCommandToken)).Methods("POST") + BaseRoutes.Commands.Handle("/delete", ApiUserRequired(deleteCommand)).Methods("POST") - sr.Handle("/test", ApiAppHandler(testCommand)).Methods("POST") - sr.Handle("/test", ApiAppHandler(testCommand)).Methods("GET") - sr.Handle("/test_e", ApiAppHandler(testEphemeralCommand)).Methods("POST") - sr.Handle("/test_e", ApiAppHandler(testEphemeralCommand)).Methods("GET") + BaseRoutes.Teams.Handle("/command_test", ApiAppHandler(testCommand)).Methods("POST") + BaseRoutes.Teams.Handle("/command_test", ApiAppHandler(testCommand)).Methods("GET") + BaseRoutes.Teams.Handle("/command_test_e", ApiAppHandler(testEphemeralCommand)).Methods("POST") + BaseRoutes.Teams.Handle("/command_test_e", ApiAppHandler(testEphemeralCommand)).Methods("GET") } func listCommands(c *Context, w http.ResponseWriter, r *http.Request) { @@ -70,7 +68,7 @@ func listCommands(c *Context, w http.ResponseWriter, r *http.Request) { } if *utils.Cfg.ServiceSettings.EnableCommands { - if result := <-Srv.Store.Command().GetByTeam(c.Session.TeamId); result.Err != nil { + if result := <-Srv.Store.Command().GetByTeam(c.TeamId); result.Err != nil { c.Err = result.Err return } else { @@ -99,7 +97,7 @@ func executeCommand(c *Context, w http.ResponseWriter, r *http.Request) { } if len(channelId) > 0 { - cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, channelId, c.Session.UserId) + cchan := Srv.Store.Channel().CheckPermissionsTo(c.TeamId, channelId, c.Session.UserId) if !c.HasPermissionsToChannel(cchan, "checkCommand") { return @@ -124,10 +122,10 @@ func executeCommand(c *Context, w http.ResponseWriter, r *http.Request) { } chanChan := Srv.Store.Channel().Get(channelId) - teamChan := Srv.Store.Team().Get(c.Session.TeamId) + teamChan := Srv.Store.Team().Get(c.TeamId) userChan := Srv.Store.User().Get(c.Session.UserId) - if result := <-Srv.Store.Command().GetByTeam(c.Session.TeamId); result.Err != nil { + if result := <-Srv.Store.Command().GetByTeam(c.TeamId); result.Err != nil { c.Err = result.Err return } else { @@ -254,7 +252,7 @@ func handleResponse(c *Context, w http.ResponseWriter, response *model.CommandRe post.Message = response.Text post.CreateAt = model.GetMillis() SendEphemeralPost( - c.Session.TeamId, + c.TeamId, c.Session.UserId, post, ) @@ -288,7 +286,7 @@ func createCommand(c *Context, w http.ResponseWriter, r *http.Request) { } cmd.CreatorId = c.Session.UserId - cmd.TeamId = c.Session.TeamId + cmd.TeamId = c.TeamId if result := <-Srv.Store.Command().Save(cmd); result.Err != nil { c.Err = result.Err @@ -315,7 +313,7 @@ func listTeamCommands(c *Context, w http.ResponseWriter, r *http.Request) { } } - if result := <-Srv.Store.Command().GetByTeam(c.Session.TeamId); result.Err != nil { + if result := <-Srv.Store.Command().GetByTeam(c.TeamId); result.Err != nil { c.Err = result.Err return } else { @@ -356,7 +354,7 @@ func regenCommandToken(c *Context, w http.ResponseWriter, r *http.Request) { } else { cmd = result.Data.(*model.Command) - if c.Session.TeamId != cmd.TeamId || (c.Session.UserId != cmd.CreatorId && !c.IsTeamAdmin()) { + if c.TeamId != cmd.TeamId || (c.Session.UserId != cmd.CreatorId && !c.IsTeamAdmin()) { c.LogAudit("fail - inappropriate permissions") c.Err = model.NewLocAppError("regenToken", "api.command.regen.app_error", nil, "user_id="+c.Session.UserId) return @@ -402,7 +400,7 @@ func deleteCommand(c *Context, w http.ResponseWriter, r *http.Request) { c.Err = result.Err return } else { - if c.Session.TeamId != result.Data.(*model.Command).TeamId || (c.Session.UserId != result.Data.(*model.Command).CreatorId && !c.IsTeamAdmin()) { + if c.TeamId != result.Data.(*model.Command).TeamId || (c.Session.UserId != result.Data.(*model.Command).CreatorId && !c.IsTeamAdmin()) { c.LogAudit("fail - inappropriate permissions") c.Err = model.NewLocAppError("deleteCommand", "api.command.delete.app_error", nil, "user_id="+c.Session.UserId) return diff --git a/api/command_echo_test.go b/api/command_echo_test.go index 3bfaa0279..26fba007c 100644 --- a/api/command_echo_test.go +++ b/api/command_echo_test.go @@ -8,23 +8,12 @@ import ( "time" "github.com/mattermost/platform/model" - "github.com/mattermost/platform/store" ) func TestEchoCommand(t *testing.T) { - Setup() - - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey+test@test.com", Nickname: "Corey Hulen", Password: "pwd"} - user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - - Client.LoginByEmail(team.Name, user1.Email, "pwd") - - channel1 := &model.Channel{DisplayName: "AA", Name: "aa" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} - channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) + th := Setup().InitBasic() + Client := th.BasicClient + channel1 := th.BasicChannel echoTestString := "/echo test" @@ -36,7 +25,7 @@ func TestEchoCommand(t *testing.T) { time.Sleep(100 * time.Millisecond) p1 := Client.Must(Client.GetPosts(channel1.Id, 0, 2, "")).Data.(*model.PostList) - if len(p1.Order) != 1 { + if len(p1.Order) != 2 { t.Fatal("Echo command failed to send") } } diff --git a/api/command_join.go b/api/command_join.go index ba3b0041e..f59925c06 100644 --- a/api/command_join.go +++ b/api/command_join.go @@ -33,7 +33,7 @@ func (me *JoinProvider) GetCommand(c *Context) *model.Command { } func (me *JoinProvider) DoCommand(c *Context, channelId string, message string) *model.CommandResponse { - if result := <-Srv.Store.Channel().GetMoreChannels(c.Session.TeamId, c.Session.UserId); result.Err != nil { + if result := <-Srv.Store.Channel().GetMoreChannels(c.TeamId, c.Session.UserId); result.Err != nil { return &model.CommandResponse{Text: c.T("api.command_join.list.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} } else { channels := result.Data.(*model.ChannelList) diff --git a/api/command_join_test.go b/api/command_join_test.go index 7260915a6..2b4a5bfe3 100644 --- a/api/command_join_test.go +++ b/api/command_join_test.go @@ -8,20 +8,13 @@ import ( "testing" "github.com/mattermost/platform/model" - "github.com/mattermost/platform/store" ) func TestJoinCommands(t *testing.T) { - Setup() - - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey+test@test.com", Nickname: "Corey Hulen", Password: "pwd"} - user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - - Client.LoginByEmail(team.Name, user1.Email, "pwd") + th := Setup().InitBasic() + Client := th.BasicClient + team := th.BasicTeam + user2 := th.BasicUser2 channel0 := &model.Channel{DisplayName: "00", Name: "00" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel0 = Client.Must(Client.CreateChannel(channel0)).Data.(*model.Channel) @@ -34,13 +27,7 @@ func TestJoinCommands(t *testing.T) { channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel) Client.Must(Client.LeaveChannel(channel2.Id)) - user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey+test@test.com", Nickname: "Corey Hulen", Password: "pwd"} - user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - - data := make(map[string]string) - data["user_id"] = user2.Id - channel3 := Client.Must(Client.CreateDirectChannel(data)).Data.(*model.Channel) + channel3 := Client.Must(Client.CreateDirectChannel(user2.Id)).Data.(*model.Channel) rs5 := Client.Must(Client.Command(channel0.Id, "/join "+channel2.Name, false)).Data.(*model.CommandResponse) if !strings.HasSuffix(rs5.GotoLocation, "/"+team.Name+"/channels/"+channel2.Name) { @@ -54,7 +41,7 @@ func TestJoinCommands(t *testing.T) { c1 := Client.Must(Client.GetChannels("")).Data.(*model.ChannelList) - if len(c1.Channels) != 5 { // 4 because of town-square, off-topic and direct + if len(c1.Channels) != 6 { // 4 because of town-square, off-topic and direct t.Fatal("didn't join channel") } diff --git a/api/command_loadtest.go b/api/command_loadtest.go index 63598c06e..2738f4b51 100644 --- a/api/command_loadtest.go +++ b/api/command_loadtest.go @@ -182,10 +182,19 @@ func (me *LoadTestProvider) SetupCommand(c *Context, channelId string, message s } } } else { + + var team *model.Team + if tr := <-Srv.Store.Team().Get(c.TeamId); tr.Err != nil { + return &model.CommandResponse{Text: "Failed to create testing environment", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} + } else { + team = tr.Data.(*model.Team) + } + client.MockSession(c.Session.Token) + client.SetTeamId(c.TeamId) CreateTestEnvironmentInTeam( client, - c.Session.TeamId, + team, utils.Range{numChannels, numChannels}, utils.Range{numUsers, numUsers}, utils.Range{numPosts, numPosts}, @@ -209,8 +218,16 @@ func (me *LoadTestProvider) UsersCommand(c *Context, channelId string, message s usersr = utils.Range{2, 5} } + var team *model.Team + if tr := <-Srv.Store.Team().Get(c.TeamId); tr.Err != nil { + return &model.CommandResponse{Text: "Failed to create testing environment", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} + } else { + team = tr.Data.(*model.Team) + } + client := model.NewClient(c.GetSiteURL()) - userCreator := NewAutoUserCreator(client, c.Session.TeamId) + client.SetTeamId(team.Id) + userCreator := NewAutoUserCreator(client, team) userCreator.Fuzzy = doFuzz userCreator.CreateTestUsers(usersr) @@ -230,9 +247,18 @@ func (me *LoadTestProvider) ChannelsCommand(c *Context, channelId string, messag if err == false { channelsr = utils.Range{2, 5} } + + var team *model.Team + if tr := <-Srv.Store.Team().Get(c.TeamId); tr.Err != nil { + return &model.CommandResponse{Text: "Failed to create testing environment", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} + } else { + team = tr.Data.(*model.Team) + } + client := model.NewClient(c.GetSiteURL()) + client.SetTeamId(team.Id) client.MockSession(c.Session.Token) - channelCreator := NewAutoChannelCreator(client, c.Session.TeamId) + channelCreator := NewAutoChannelCreator(client, team) channelCreator.Fuzzy = doFuzz channelCreator.CreateTestChannels(channelsr) @@ -262,7 +288,7 @@ func (me *LoadTestProvider) PostsCommand(c *Context, channelId string, message s } var usernames []string - if result := <-Srv.Store.User().GetProfiles(c.Session.TeamId); result.Err == nil { + if result := <-Srv.Store.User().GetProfiles(c.TeamId); result.Err == nil { profileUsers := result.Data.(map[string]*model.User) usernames = make([]string, len(profileUsers)) i := 0 @@ -273,6 +299,7 @@ func (me *LoadTestProvider) PostsCommand(c *Context, channelId string, message s } client := model.NewClient(c.GetSiteURL()) + client.SetTeamId(c.TeamId) client.MockSession(c.Session.Token) testPoster := NewAutoPostCreator(client, channelId) testPoster.Fuzzy = doFuzz diff --git a/api/command_loadtest_test.go b/api/command_loadtest_test.go index 4988050a1..8c138842e 100644 --- a/api/command_loadtest_test.go +++ b/api/command_loadtest_test.go @@ -9,12 +9,14 @@ import ( "time" "github.com/mattermost/platform/model" - "github.com/mattermost/platform/store" "github.com/mattermost/platform/utils" ) func TestLoadTestHelpCommands(t *testing.T) { - Setup() + th := Setup().InitBasic() + Client := th.BasicClient + channel := th.BasicChannel + // enable testing to use /loadtest but don't save it since we don't want to overwrite config.json enableTesting := utils.Cfg.ServiceSettings.EnableTesting defer func() { @@ -23,18 +25,6 @@ func TestLoadTestHelpCommands(t *testing.T) { utils.Cfg.ServiceSettings.EnableTesting = true - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey+test@test.com", Nickname: "Corey Hulen", Password: "pwd"} - user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - - Client.LoginByEmail(team.Name, user1.Email, "pwd") - - channel := &model.Channel{DisplayName: "00", Name: "00" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} - channel = Client.Must(Client.CreateChannel(channel)).Data.(*model.Channel) - rs := Client.Must(Client.Command(channel.Id, "/loadtest help", false)).Data.(*model.CommandResponse) if !strings.Contains(rs.Text, "Mattermost load testing commands to help") { t.Fatal(rs.Text) @@ -44,7 +34,10 @@ func TestLoadTestHelpCommands(t *testing.T) { } func TestLoadTestSetupCommands(t *testing.T) { - Setup() + th := Setup().InitBasic() + Client := th.BasicClient + channel := th.BasicChannel + // enable testing to use /loadtest but don't save it since we don't want to overwrite config.json enableTesting := utils.Cfg.ServiceSettings.EnableTesting defer func() { @@ -53,18 +46,6 @@ func TestLoadTestSetupCommands(t *testing.T) { utils.Cfg.ServiceSettings.EnableTesting = true - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey+test@test.com", Nickname: "Corey Hulen", Password: "pwd"} - user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - - Client.LoginByEmail(team.Name, user1.Email, "pwd") - - channel := &model.Channel{DisplayName: "00", Name: "00" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} - channel = Client.Must(Client.CreateChannel(channel)).Data.(*model.Channel) - rs := Client.Must(Client.Command(channel.Id, "/loadtest setup fuzz 1 1 1", false)).Data.(*model.CommandResponse) if rs.Text != "Creating enviroment..." { t.Fatal(rs.Text) @@ -74,7 +55,10 @@ func TestLoadTestSetupCommands(t *testing.T) { } func TestLoadTestUsersCommands(t *testing.T) { - Setup() + th := Setup().InitBasic() + Client := th.BasicClient + channel := th.BasicChannel + // enable testing to use /loadtest but don't save it since we don't want to overwrite config.json enableTesting := utils.Cfg.ServiceSettings.EnableTesting defer func() { @@ -83,18 +67,6 @@ func TestLoadTestUsersCommands(t *testing.T) { utils.Cfg.ServiceSettings.EnableTesting = true - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey+test@test.com", Nickname: "Corey Hulen", Password: "pwd"} - user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - - Client.LoginByEmail(team.Name, user1.Email, "pwd") - - channel := &model.Channel{DisplayName: "00", Name: "00" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} - channel = Client.Must(Client.CreateChannel(channel)).Data.(*model.Channel) - rs := Client.Must(Client.Command(channel.Id, "/loadtest users fuzz 1 2", false)).Data.(*model.CommandResponse) if rs.Text != "Adding users..." { t.Fatal(rs.Text) @@ -104,7 +76,10 @@ func TestLoadTestUsersCommands(t *testing.T) { } func TestLoadTestChannelsCommands(t *testing.T) { - Setup() + th := Setup().InitBasic() + Client := th.BasicClient + channel := th.BasicChannel + // enable testing to use /loadtest but don't save it since we don't want to overwrite config.json enableTesting := utils.Cfg.ServiceSettings.EnableTesting defer func() { @@ -113,18 +88,6 @@ func TestLoadTestChannelsCommands(t *testing.T) { utils.Cfg.ServiceSettings.EnableTesting = true - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey+test@test.com", Nickname: "Corey Hulen", Password: "pwd"} - user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - - Client.LoginByEmail(team.Name, user1.Email, "pwd") - - channel := &model.Channel{DisplayName: "00", Name: "00" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} - channel = Client.Must(Client.CreateChannel(channel)).Data.(*model.Channel) - rs := Client.Must(Client.Command(channel.Id, "/loadtest channels fuzz 1 2", false)).Data.(*model.CommandResponse) if rs.Text != "Adding channels..." { t.Fatal(rs.Text) @@ -134,7 +97,10 @@ func TestLoadTestChannelsCommands(t *testing.T) { } func TestLoadTestPostsCommands(t *testing.T) { - Setup() + th := Setup().InitBasic() + Client := th.BasicClient + channel := th.BasicChannel + // enable testing to use /loadtest but don't save it since we don't want to overwrite config.json enableTesting := utils.Cfg.ServiceSettings.EnableTesting defer func() { @@ -143,18 +109,6 @@ func TestLoadTestPostsCommands(t *testing.T) { utils.Cfg.ServiceSettings.EnableTesting = true - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey+test@test.com", Nickname: "Corey Hulen", Password: "pwd"} - user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - - Client.LoginByEmail(team.Name, user1.Email, "pwd") - - channel := &model.Channel{DisplayName: "00", Name: "00" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} - channel = Client.Must(Client.CreateChannel(channel)).Data.(*model.Channel) - rs := Client.Must(Client.Command(channel.Id, "/loadtest posts fuzz 2 3 2", false)).Data.(*model.CommandResponse) if rs.Text != "Adding posts..." { t.Fatal(rs.Text) @@ -164,7 +118,10 @@ func TestLoadTestPostsCommands(t *testing.T) { } func TestLoadTestUrlCommands(t *testing.T) { - Setup() + th := Setup().InitBasic() + Client := th.BasicClient + channel := th.BasicChannel + // enable testing to use /loadtest but don't save it since we don't want to overwrite config.json enableTesting := utils.Cfg.ServiceSettings.EnableTesting defer func() { @@ -173,18 +130,6 @@ func TestLoadTestUrlCommands(t *testing.T) { utils.Cfg.ServiceSettings.EnableTesting = true - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey+test@test.com", Nickname: "Corey Hulen", Password: "pwd"} - user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - - Client.LoginByEmail(team.Name, user1.Email, "pwd") - - channel := &model.Channel{DisplayName: "00", Name: "00" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} - channel = Client.Must(Client.CreateChannel(channel)).Data.(*model.Channel) - command := "/loadtest url " if r := Client.Must(Client.Command(channel.Id, command, false)).Data.(*model.CommandResponse); r.Text != "Command must contain a url" { t.Fatal("/loadtest url with no url should've failed") @@ -223,7 +168,10 @@ func TestLoadTestUrlCommands(t *testing.T) { } func TestLoadTestJsonCommands(t *testing.T) { - Setup() + th := Setup().InitBasic() + Client := th.BasicClient + channel := th.BasicChannel + // enable testing to use /loadtest but don't save it since we don't want to overwrite config.json enableTesting := utils.Cfg.ServiceSettings.EnableTesting defer func() { @@ -232,18 +180,6 @@ func TestLoadTestJsonCommands(t *testing.T) { utils.Cfg.ServiceSettings.EnableTesting = true - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey+test@test.com", Nickname: "Corey Hulen", Password: "pwd"} - user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - - Client.LoginByEmail(team.Name, user1.Email, "pwd") - - channel := &model.Channel{DisplayName: "00", Name: "00" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} - channel = Client.Must(Client.CreateChannel(channel)).Data.(*model.Channel) - command := "/loadtest json " if r := Client.Must(Client.Command(channel.Id, command, false)).Data.(*model.CommandResponse); r.Text != "Command must contain a url" { t.Fatal("/loadtest url with no url should've failed") @@ -255,9 +191,9 @@ func TestLoadTestJsonCommands(t *testing.T) { t.Fatal("/loadtest url with invalid url should've failed") } - command = "/loadtest url https://secure.beldienst.nl/test.json" // Chicken-egg so will replace with mattermost/platform URL soon + command = "/loadtest json test-slack-attachments" if r := Client.Must(Client.Command(channel.Id, command, false)).Data.(*model.CommandResponse); r.Text != "Loading data..." { - t.Fatal("/loadtest url for README.md should've executed") + t.Fatal("/loadtest json should've executed") } time.Sleep(2 * time.Second) diff --git a/api/command_logout_test.go b/api/command_logout_test.go index eee7520a8..eec959115 100644 --- a/api/command_logout_test.go +++ b/api/command_logout_test.go @@ -8,25 +8,12 @@ import ( "testing" "github.com/mattermost/platform/model" - "github.com/mattermost/platform/store" ) func TestLogoutTestCommand(t *testing.T) { - Setup() + th := Setup().InitBasic() - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey+test@test.com", Nickname: "Corey Hulen", Password: "pwd"} - user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - - Client.LoginByEmail(team.Name, user1.Email, "pwd") - - channel1 := &model.Channel{DisplayName: "AA", Name: "aa" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} - channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) - - rs1 := Client.Must(Client.Command(channel1.Id, "/logout", false)).Data.(*model.CommandResponse) + rs1 := th.BasicClient.Must(th.BasicClient.Command(th.BasicChannel.Id, "/logout", false)).Data.(*model.CommandResponse) if !strings.HasSuffix(rs1.GotoLocation, "logout") { t.Fatal("failed to logout") } diff --git a/api/command_me_test.go b/api/command_me_test.go index d55a15b2c..f466f5764 100644 --- a/api/command_me_test.go +++ b/api/command_me_test.go @@ -8,35 +8,24 @@ import ( "time" "github.com/mattermost/platform/model" - "github.com/mattermost/platform/store" ) func TestMeCommand(t *testing.T) { - Setup() - - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey+test@test.com", Nickname: "Corey Hulen", Password: "pwd"} - user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - - Client.LoginByEmail(team.Name, user1.Email, "pwd") - - channel1 := &model.Channel{DisplayName: "AA", Name: "aa" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} - channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) + th := Setup().InitBasic() + Client := th.BasicClient + channel := th.BasicChannel testString := "/me hello" - r1 := Client.Must(Client.Command(channel1.Id, testString, false)).Data.(*model.CommandResponse) + r1 := Client.Must(Client.Command(channel.Id, testString, false)).Data.(*model.CommandResponse) if r1 == nil { t.Fatal("Command failed to execute") } time.Sleep(100 * time.Millisecond) - p1 := Client.Must(Client.GetPosts(channel1.Id, 0, 2, "")).Data.(*model.PostList) - if len(p1.Order) != 1 { + p1 := Client.Must(Client.GetPosts(channel.Id, 0, 2, "")).Data.(*model.PostList) + if len(p1.Order) != 2 { t.Fatal("Command failed to send") } else { if p1.Posts[p1.Order[0]].Message != `*hello*` { diff --git a/api/command_msg.go b/api/command_msg.go index 273a45be9..517999695 100644 --- a/api/command_msg.go +++ b/api/command_msg.go @@ -46,7 +46,7 @@ func (me *msgProvider) DoCommand(c *Context, channelId string, message string) * targetUser = strings.SplitN(message, " ", 2)[0] targetUser = strings.TrimPrefix(targetUser, "@") - if profileList := <-Srv.Store.User().GetProfiles(c.Session.TeamId); profileList.Err != nil { + if profileList := <-Srv.Store.User().GetProfiles(c.TeamId); profileList.Err != nil { c.Err = profileList.Err return &model.CommandResponse{Text: c.T("api.command_msg.list.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} } else { @@ -62,7 +62,7 @@ func (me *msgProvider) DoCommand(c *Context, channelId string, message string) * //Find the channel based on this user channelName := model.GetDMNameFromIds(c.Session.UserId, userProfile.Id) - if channel := <-Srv.Store.Channel().GetByName(c.Session.TeamId, channelName); channel.Err != nil { + if channel := <-Srv.Store.Channel().GetByName(c.TeamId, channelName); channel.Err != nil { if channel.Err.Id == "store.sql_channel.get_by_name.missing.app_error" { if directChannel, err := CreateDirectChannel(c, userProfile.Id); err != nil { c.Err = err @@ -78,7 +78,7 @@ func (me *msgProvider) DoCommand(c *Context, channelId string, message string) * targetChannelId = channel.Data.(*model.Channel).Id } - makeDirectChannelVisible(c.Session.TeamId, targetChannelId) + makeDirectChannelVisible(c.TeamId, targetChannelId) if len(parsedMessage) > 0 { post := &model.Post{} post.Message = parsedMessage @@ -87,9 +87,11 @@ func (me *msgProvider) DoCommand(c *Context, channelId string, message string) * return &model.CommandResponse{Text: c.T("api.command_msg.fail.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} } } + return &model.CommandResponse{GotoLocation: c.GetTeamURL() + "/channels/" + channelName, Text: c.T("api.command_msg.success"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} } } } + return &model.CommandResponse{Text: c.T("api.command_msg.missing.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} } diff --git a/api/command_msg_test.go b/api/command_msg_test.go index 222a401fd..db8c3216c 100644 --- a/api/command_msg_test.go +++ b/api/command_msg_test.go @@ -4,39 +4,29 @@ package api import ( + "github.com/mattermost/platform/model" "strings" "testing" - - "github.com/mattermost/platform/model" - "github.com/mattermost/platform/store" ) func TestMsgCommands(t *testing.T) { - Setup() - - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "success+test@simulator.amazonses.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Username: "user1", Password: "pwd"} - user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - - Client.LoginByEmail(team.Name, user1.Email, "pwd") - - user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test2@simulator.amazonses.com", Nickname: "Corey Hulen 2", Username: "user2", Password: "pwd"} - user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user2.Id)) - - user3 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test3@simulator.amazonses.com", Nickname: "Corey Hulen 3", Username: "user3", Password: "pwd"} - user3 = Client.Must(Client.CreateUser(user3, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user3.Id)) - - rs1 := Client.Must(Client.Command("", "/msg user2", false)).Data.(*model.CommandResponse) + th := Setup().InitBasic() + Client := th.BasicClient + team := th.BasicTeam + user1 := th.BasicUser + user2 := th.BasicUser2 + user3 := th.CreateUser(th.BasicClient) + LinkUserToTeam(user3, team) + + Client.Must(Client.CreateDirectChannel(user2.Id)) + Client.Must(Client.CreateDirectChannel(user3.Id)) + + rs1 := Client.Must(Client.Command("", "/msg "+user2.Username, false)).Data.(*model.CommandResponse) if !strings.HasSuffix(rs1.GotoLocation, "/"+team.Name+"/channels/"+user1.Id+"__"+user2.Id) && !strings.HasSuffix(rs1.GotoLocation, "/"+team.Name+"/channels/"+user2.Id+"__"+user1.Id) { t.Fatal("failed to create direct channel") } - rs2 := Client.Must(Client.Command("", "/msg user3 foobar", false)).Data.(*model.CommandResponse) + rs2 := Client.Must(Client.Command("", "/msg "+user3.Username+" foobar", false)).Data.(*model.CommandResponse) if !strings.HasSuffix(rs2.GotoLocation, "/"+team.Name+"/channels/"+user1.Id+"__"+user3.Id) && !strings.HasSuffix(rs2.GotoLocation, "/"+team.Name+"/channels/"+user3.Id+"__"+user1.Id) { t.Fatal("failed to create second direct channel") } @@ -44,7 +34,7 @@ func TestMsgCommands(t *testing.T) { t.Fatalf("post did not get sent to direct message") } - rs3 := Client.Must(Client.Command("", "/msg user2", false)).Data.(*model.CommandResponse) + rs3 := Client.Must(Client.Command("", "/msg "+user2.Username, false)).Data.(*model.CommandResponse) if !strings.HasSuffix(rs3.GotoLocation, "/"+team.Name+"/channels/"+user1.Id+"__"+user2.Id) && !strings.HasSuffix(rs3.GotoLocation, "/"+team.Name+"/channels/"+user2.Id+"__"+user1.Id) { t.Fatal("failed to go back to existing direct channel") } diff --git a/api/command_shrug_test.go b/api/command_shrug_test.go index 92cecf664..99c10d191 100644 --- a/api/command_shrug_test.go +++ b/api/command_shrug_test.go @@ -8,35 +8,24 @@ import ( "time" "github.com/mattermost/platform/model" - "github.com/mattermost/platform/store" ) func TestShrugCommand(t *testing.T) { - Setup() - - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey+test@test.com", Nickname: "Corey Hulen", Password: "pwd"} - user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - - Client.LoginByEmail(team.Name, user1.Email, "pwd") - - channel1 := &model.Channel{DisplayName: "AA", Name: "aa" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} - channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) + th := Setup().InitBasic() + Client := th.BasicClient + channel := th.BasicChannel testString := "/shrug" - r1 := Client.Must(Client.Command(channel1.Id, testString, false)).Data.(*model.CommandResponse) + r1 := Client.Must(Client.Command(channel.Id, testString, false)).Data.(*model.CommandResponse) if r1 == nil { t.Fatal("Command failed to execute") } time.Sleep(100 * time.Millisecond) - p1 := Client.Must(Client.GetPosts(channel1.Id, 0, 2, "")).Data.(*model.PostList) - if len(p1.Order) != 1 { + p1 := Client.Must(Client.GetPosts(channel.Id, 0, 2, "")).Data.(*model.PostList) + if len(p1.Order) != 2 { t.Fatal("Command failed to send") } else { if p1.Posts[p1.Order[0]].Message != `¯\\\_(ツ)\_/¯` { diff --git a/api/command_test.go b/api/command_test.go index 22e2bd666..c6500c6cf 100644 --- a/api/command_test.go +++ b/api/command_test.go @@ -8,21 +8,12 @@ import ( "time" "github.com/mattermost/platform/model" - "github.com/mattermost/platform/store" "github.com/mattermost/platform/utils" ) func TestListCommands(t *testing.T) { - Setup() - - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - - Client.LoginByEmail(team.Name, user1.Email, "pwd") + th := Setup().InitBasic() + Client := th.BasicClient if results, err := Client.ListCommands(); err != nil { t.Fatal(err) @@ -43,7 +34,10 @@ func TestListCommands(t *testing.T) { } func TestCreateCommand(t *testing.T) { - Setup() + th := Setup().InitBasic().InitSystemAdmin() + Client := th.BasicClient + user := th.SystemAdminUser + team := th.SystemAdminTeam enableCommands := *utils.Cfg.ServiceSettings.EnableCommands defer func() { @@ -51,26 +45,13 @@ func TestCreateCommand(t *testing.T) { }() *utils.Cfg.ServiceSettings.EnableCommands = true - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey+test@test.com", Nickname: "Corey Hulen", Password: "pwd"} - user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user.Id)) - - Client.LoginByEmail(team.Name, user.Email, "pwd") - - cmd := &model.Command{URL: "http://nowhere.com", Method: model.COMMAND_METHOD_POST} + cmd := &model.Command{URL: "http://nowhere.com", Method: model.COMMAND_METHOD_POST, Trigger: "trigger"} if _, err := Client.CreateCommand(cmd); err == nil { t.Fatal("should have failed because not admin") } - c := &Context{} - c.RequestId = model.NewId() - c.IpAddress = "cmd_line" - UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN) - Client.LoginByEmail(team.Name, user.Email, "pwd") + Client = th.SystemAdminClient var rcmd *model.Command if result, err := Client.CreateCommand(cmd); err != nil { @@ -87,7 +68,7 @@ func TestCreateCommand(t *testing.T) { t.Fatal("team ids didn't match") } - cmd = &model.Command{CreatorId: "123", TeamId: "456", URL: "http://nowhere.com", Method: model.COMMAND_METHOD_POST} + cmd = &model.Command{CreatorId: "123", TeamId: "456", URL: "http://nowhere.com", Method: model.COMMAND_METHOD_POST, Trigger: "trigger"} if result, err := Client.CreateCommand(cmd); err != nil { t.Fatal(err) } else { @@ -101,27 +82,16 @@ func TestCreateCommand(t *testing.T) { } func TestListTeamCommands(t *testing.T) { - Setup() + th := Setup().InitSystemAdmin() + Client := th.SystemAdminClient + enableCommands := *utils.Cfg.ServiceSettings.EnableCommands defer func() { utils.Cfg.ServiceSettings.EnableCommands = &enableCommands }() *utils.Cfg.ServiceSettings.EnableCommands = true - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user.Id)) - - c := &Context{} - c.RequestId = model.NewId() - c.IpAddress = "cmd_line" - UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN) - Client.LoginByEmail(team.Name, user.Email, "pwd") - - cmd1 := &model.Command{URL: "http://nowhere.com", Method: model.COMMAND_METHOD_POST} + cmd1 := &model.Command{URL: "http://nowhere.com", Method: model.COMMAND_METHOD_POST, Trigger: "trigger"} cmd1 = Client.Must(Client.CreateCommand(cmd1)).Data.(*model.Command) if result, err := Client.ListTeamCommands(); err != nil { @@ -136,27 +106,16 @@ func TestListTeamCommands(t *testing.T) { } func TestRegenToken(t *testing.T) { - Setup() + th := Setup().InitSystemAdmin() + Client := th.SystemAdminClient + enableCommands := *utils.Cfg.ServiceSettings.EnableCommands defer func() { utils.Cfg.ServiceSettings.EnableCommands = &enableCommands }() *utils.Cfg.ServiceSettings.EnableCommands = true - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey+test@test.com", Nickname: "Corey Hulen", Password: "pwd"} - user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user.Id)) - - c := &Context{} - c.RequestId = model.NewId() - c.IpAddress = "cmd_line" - UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN) - Client.LoginByEmail(team.Name, user.Email, "pwd") - - cmd := &model.Command{URL: "http://nowhere.com", Method: model.COMMAND_METHOD_POST} + cmd := &model.Command{URL: "http://nowhere.com", Method: model.COMMAND_METHOD_POST, Trigger: "trigger"} cmd = Client.Must(Client.CreateCommand(cmd)).Data.(*model.Command) data := make(map[string]string) @@ -172,27 +131,16 @@ func TestRegenToken(t *testing.T) { } func TestDeleteCommand(t *testing.T) { - Setup() + th := Setup().InitSystemAdmin() + Client := th.SystemAdminClient + enableCommands := *utils.Cfg.ServiceSettings.EnableCommands defer func() { utils.Cfg.ServiceSettings.EnableCommands = &enableCommands }() *utils.Cfg.ServiceSettings.EnableCommands = true - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user.Id)) - - c := &Context{} - c.RequestId = model.NewId() - c.IpAddress = "cmd_line" - UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN) - Client.LoginByEmail(team.Name, user.Email, "pwd") - - cmd := &model.Command{URL: "http://nowhere.com", Method: model.COMMAND_METHOD_POST} + cmd := &model.Command{URL: "http://nowhere.com", Method: model.COMMAND_METHOD_POST, Trigger: "trigger"} cmd = Client.Must(Client.CreateCommand(cmd)).Data.(*model.Command) data := make(map[string]string) @@ -209,31 +157,18 @@ func TestDeleteCommand(t *testing.T) { } func TestTestCommand(t *testing.T) { - Setup() + th := Setup().InitSystemAdmin() + Client := th.SystemAdminClient + channel1 := th.SystemAdminChannel + enableCommands := *utils.Cfg.ServiceSettings.EnableCommands defer func() { utils.Cfg.ServiceSettings.EnableCommands = &enableCommands }() *utils.Cfg.ServiceSettings.EnableCommands = true - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user.Id)) - - c := &Context{} - c.RequestId = model.NewId() - c.IpAddress = "cmd_line" - UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN) - Client.LoginByEmail(team.Name, user.Email, "pwd") - - channel1 := &model.Channel{DisplayName: "AA", Name: "aa" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} - channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) - cmd1 := &model.Command{ - URL: "http://localhost" + utils.Cfg.ServiceSettings.ListenAddress + "/api/v1/commands/test", + URL: "http://localhost" + utils.Cfg.ServiceSettings.ListenAddress + model.API_URL_SUFFIX + "/teams/command_test", Method: model.COMMAND_METHOD_POST, Trigger: "test", } @@ -253,7 +188,7 @@ func TestTestCommand(t *testing.T) { } cmd2 := &model.Command{ - URL: "http://localhost" + utils.Cfg.ServiceSettings.ListenAddress + "/api/v1/commands/test", + URL: "http://localhost" + utils.Cfg.ServiceSettings.ListenAddress + model.API_URL_SUFFIX + "/teams/command_test", Method: model.COMMAND_METHOD_GET, Trigger: "test2", } diff --git a/api/context.go b/api/context.go index 56c8c86d2..8bbd5a1d2 100644 --- a/api/context.go +++ b/api/context.go @@ -11,10 +11,12 @@ import ( "strings" l4g "github.com/alecthomas/log4go" + "github.com/gorilla/mux" + goi18n "github.com/nicksnyder/go-i18n/i18n" + "github.com/mattermost/platform/model" "github.com/mattermost/platform/store" "github.com/mattermost/platform/utils" - goi18n "github.com/nicksnyder/go-i18n/i18n" ) var sessionCache *utils.Cache = utils.NewLru(model.SESSION_CACHE_SIZE) @@ -39,6 +41,7 @@ type Context struct { siteURL string T goi18n.TranslateFunc Locale string + TeamId string } func ApiAppHandler(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler { @@ -94,6 +97,8 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { c.T, c.Locale = utils.GetTranslationsAndLocale(w, r) c.RequestId = model.NewId() c.IpAddress = GetIpAddress(r) + c.TeamId = mux.Vars(r)["team_id"] + h.isApi = IsApiCall(r) token := "" isTokenFromQueryString := false @@ -116,7 +121,7 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if (h.requireSystemAdmin || h.requireUser) && !h.trustRequester { if r.Header.Get(model.HEADER_REQUESTED_WITH) != model.HEADER_REQUESTED_WITH_XML { - c.Err = model.NewLocAppError("ServeHTTP", "api.context.session_expired.app_error", nil, "token="+token) + c.Err = model.NewLocAppError("ServeHTTP", "api.context.session_expired.app_error", nil, "token="+token+" Appears to bea CSRF attempt") token = "" } } @@ -182,6 +187,10 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { c.SystemAdminRequired() } + if c.Err == nil && len(c.TeamId) > 0 { + c.HasPermissionsToTeam(c.TeamId, "TeamRoute") + } + if c.Err == nil && h.isUserActivity && token != "" && len(c.Session.UserId) > 0 { go func() { if err := (<-Srv.Store.User().UpdateUserAndSessionActivity(c.Session.UserId, c.Session.Id, model.GetMillis())).Err; err != nil { @@ -308,13 +317,14 @@ func (c *Context) HasPermissionsToUser(userId string, where string) bool { } func (c *Context) HasPermissionsToTeam(teamId string, where string) bool { - if c.Session.TeamId == teamId { + if c.IsSystemAdmin() { return true } - // You're a mattermost system admin and you're on the VPN - if c.IsSystemAdmin() { - return true + for _, teamMember := range c.Session.TeamMembers { + if teamId == teamMember.TeamId { + return true + } } c.Err = model.NewLocAppError(where, "api.context.permissions.app_error", nil, "userId="+c.Session.UserId+", teamId="+teamId) @@ -353,10 +363,17 @@ func (c *Context) IsSystemAdmin() bool { } func (c *Context) IsTeamAdmin() bool { - if model.IsInRole(c.Session.Roles, model.ROLE_TEAM_ADMIN) || c.IsSystemAdmin() { + + if c.IsSystemAdmin() { return true } - return false + + team := c.Session.GetTeamByTeamId(c.TeamId) + if team == nil { + return false + } + + return model.IsInRole(team.Roles, model.ROLE_TEAM_ADMIN) } func (c *Context) RemoveSessionCookie(w http.ResponseWriter, r *http.Request) { @@ -386,7 +403,7 @@ func (c *Context) setTeamURL(url string, valid bool) { } func (c *Context) SetTeamURLFromSession() { - if result := <-Srv.Store.Team().Get(c.Session.TeamId); result.Err == nil { + if result := <-Srv.Store.Team().Get(c.TeamId); result.Err == nil { c.setTeamURL(c.GetSiteURL()+"/"+result.Data.(*model.Team).Name, true) } } @@ -413,6 +430,10 @@ func (c *Context) GetSiteURL() string { return c.siteURL } +func IsApiCall(r *http.Request) bool { + return strings.Index(r.URL.Path, "/api/") == 0 +} + func GetIpAddress(r *http.Request) string { address := r.Header.Get(model.HEADER_FORWARDED) @@ -427,69 +448,69 @@ func GetIpAddress(r *http.Request) string { return address } -func IsTestDomain(r *http.Request) bool { +// func IsTestDomain(r *http.Request) bool { - if strings.Index(r.Host, "localhost") == 0 { - return true - } +// if strings.Index(r.Host, "localhost") == 0 { +// return true +// } - if strings.Index(r.Host, "dockerhost") == 0 { - return true - } +// if strings.Index(r.Host, "dockerhost") == 0 { +// return true +// } - if strings.Index(r.Host, "test") == 0 { - return true - } +// if strings.Index(r.Host, "test") == 0 { +// return true +// } - if strings.Index(r.Host, "127.0.") == 0 { - return true - } +// if strings.Index(r.Host, "127.0.") == 0 { +// return true +// } - if strings.Index(r.Host, "192.168.") == 0 { - return true - } +// if strings.Index(r.Host, "192.168.") == 0 { +// return true +// } - if strings.Index(r.Host, "10.") == 0 { - return true - } +// if strings.Index(r.Host, "10.") == 0 { +// return true +// } - if strings.Index(r.Host, "176.") == 0 { - return true - } +// if strings.Index(r.Host, "176.") == 0 { +// return true +// } - return false -} +// return false +// } -func IsBetaDomain(r *http.Request) bool { +// func IsBetaDomain(r *http.Request) bool { - if strings.Index(r.Host, "beta") == 0 { - return true - } +// if strings.Index(r.Host, "beta") == 0 { +// return true +// } - if strings.Index(r.Host, "ci") == 0 { - return true - } +// if strings.Index(r.Host, "ci") == 0 { +// return true +// } - return false -} +// return false +// } -var privateIpAddress = []*net.IPNet{ - {IP: net.IPv4(10, 0, 0, 1), Mask: net.IPv4Mask(255, 0, 0, 0)}, - {IP: net.IPv4(176, 16, 0, 1), Mask: net.IPv4Mask(255, 255, 0, 0)}, - {IP: net.IPv4(192, 168, 0, 1), Mask: net.IPv4Mask(255, 255, 255, 0)}, - {IP: net.IPv4(127, 0, 0, 1), Mask: net.IPv4Mask(255, 255, 255, 252)}, -} +// var privateIpAddress = []*net.IPNet{ +// {IP: net.IPv4(10, 0, 0, 1), Mask: net.IPv4Mask(255, 0, 0, 0)}, +// {IP: net.IPv4(176, 16, 0, 1), Mask: net.IPv4Mask(255, 255, 0, 0)}, +// {IP: net.IPv4(192, 168, 0, 1), Mask: net.IPv4Mask(255, 255, 255, 0)}, +// {IP: net.IPv4(127, 0, 0, 1), Mask: net.IPv4Mask(255, 255, 255, 252)}, +// } -func IsPrivateIpAddress(ipAddress string) bool { +// func IsPrivateIpAddress(ipAddress string) bool { - for _, pips := range privateIpAddress { - if pips.Contains(net.ParseIP(ipAddress)) { - return true - } - } +// for _, pips := range privateIpAddress { +// if pips.Contains(net.ParseIP(ipAddress)) { +// return true +// } +// } - return false -} +// return false +// } func RenderWebError(err *model.AppError, w http.ResponseWriter, r *http.Request) { T, _ := utils.GetTranslationsAndLocale(w, r) @@ -513,9 +534,17 @@ func RenderWebError(err *model.AppError, w http.ResponseWriter, r *http.Request) func Handle404(w http.ResponseWriter, r *http.Request) { err := model.NewLocAppError("Handle404", "api.context.404.app_error", nil, "") + err.Translate(utils.T) err.StatusCode = http.StatusNotFound l4g.Error("%v: code=404 ip=%v", r.URL.Path, GetIpAddress(r)) - RenderWebError(err, w, r) + + if IsApiCall(r) { + w.WriteHeader(err.StatusCode) + err.DetailedError = "There doesn't appear to be an api call for the url='" + r.URL.Path + "'. Typo? are you missing a team_id or user_id as part of the url?" + w.Write([]byte(err.ToJson())) + } else { + RenderWebError(err, w, r) + } } func GetSession(token string) *model.Session { @@ -542,6 +571,20 @@ func GetSession(token string) *model.Session { return session } +func RemoveAllSessionsForUserId(userId string) { + + keys := sessionCache.Keys() + + for _, key := range keys { + if ts, ok := sessionCache.Get(key); ok { + session := ts.(*model.Session) + if session.UserId == userId { + sessionCache.Remove(key) + } + } + } +} + func AddSessionToCache(session *model.Session) { sessionCache.AddWithExpiresInSecs(session.Token, session, int64(*utils.Cfg.ServiceSettings.SessionCacheInMinutes*60)) } diff --git a/api/context_test.go b/api/context_test.go index a9e2afa0f..c3c7a9768 100644 --- a/api/context_test.go +++ b/api/context_test.go @@ -8,32 +8,6 @@ import ( "testing" ) -var ipAddressTests = []struct { - address string - expected bool -}{ - {"126.255.255.255", false}, - {"127.0.0.1", true}, - {"127.0.0.4", false}, - {"9.255.255.255", false}, - {"10.0.0.1", true}, - {"11.0.0.1", false}, - {"176.15.155.255", false}, - {"176.16.0.1", true}, - {"176.31.0.1", false}, - {"192.167.255.255", false}, - {"192.168.0.1", true}, - {"192.169.0.1", false}, -} - -func TestIpAddress(t *testing.T) { - for _, v := range ipAddressTests { - if IsPrivateIpAddress(v.address) != v.expected { - t.Errorf("expect %v as %v", v.address, v.expected) - } - } -} - func TestContext(t *testing.T) { context := Context{} @@ -52,9 +26,26 @@ func TestContext(t *testing.T) { if !context.HasPermissionsToUser("6", "") { t.Fatal("should have permissions") } +} - // context.IpAddress = "125.0.0.1" - // if context.HasPermissionsToUser("6", "") { - // t.Fatal("shouldn't have permissions") - // } +func TestCache(t *testing.T) { + session := &model.Session{ + Id: model.NewId(), + Token: model.NewId(), + UserId: model.NewId(), + } + + sessionCache.AddWithExpiresInSecs(session.Token, session, 5*60) + + keys := sessionCache.Keys() + if len(keys) <= 0 { + t.Fatal("should have items") + } + + RemoveAllSessionsForUserId(session.UserId) + + rkeys := sessionCache.Keys() + if len(rkeys) != len(keys)-1 { + t.Fatal("should have one less") + } } diff --git a/api/export.go b/api/export.go index f2f8f87ab..da066379f 100644 --- a/api/export.go +++ b/api/export.go @@ -60,7 +60,7 @@ func ExportToFile(options *ExportOptions) (link string, err *model.AppError) { ExportToWriter(file, options) } - return "/api/v1/files/get_export", nil + return model.API_URL_SUFFIX + "/files/get_export", nil } func ExportToWriter(w io.Writer, options *ExportOptions) *model.AppError { diff --git a/api/file.go b/api/file.go index 991516bed..c51a4a046 100644 --- a/api/file.go +++ b/api/file.go @@ -57,15 +57,14 @@ const ( var fileInfoCache *utils.Cache = utils.NewLru(1000) -func InitFile(r *mux.Router) { +func InitFile() { l4g.Debug(utils.T("api.file.init.debug")) - sr := r.PathPrefix("/files").Subrouter() - sr.Handle("/upload", ApiUserRequired(uploadFile)).Methods("POST") - sr.Handle("/get/{channel_id:[A-Za-z0-9]+}/{user_id:[A-Za-z0-9]+}/{filename:([A-Za-z0-9]+/)?.+(\\.[A-Za-z0-9]{3,})?}", ApiAppHandlerTrustRequester(getFile)).Methods("GET") - sr.Handle("/get_info/{channel_id:[A-Za-z0-9]+}/{user_id:[A-Za-z0-9]+}/{filename:([A-Za-z0-9]+/)?.+(\\.[A-Za-z0-9]{3,})?}", ApiAppHandler(getFileInfo)).Methods("GET") - sr.Handle("/get_public_link", ApiUserRequired(getPublicLink)).Methods("POST") - sr.Handle("/get_export", ApiUserRequired(getExport)).Methods("GET") + BaseRoutes.Files.Handle("/upload", ApiUserRequired(uploadFile)).Methods("POST") + BaseRoutes.Files.Handle("/get/{channel_id:[A-Za-z0-9]+}/{user_id:[A-Za-z0-9]+}/{filename:([A-Za-z0-9]+/)?.+(\\.[A-Za-z0-9]{3,})?}", ApiAppHandlerTrustRequester(getFile)).Methods("GET") + BaseRoutes.Files.Handle("/get_info/{channel_id:[A-Za-z0-9]+}/{user_id:[A-Za-z0-9]+}/{filename:([A-Za-z0-9]+/)?.+(\\.[A-Za-z0-9]{3,})?}", ApiAppHandler(getFileInfo)).Methods("GET") + BaseRoutes.Files.Handle("/get_public_link", ApiUserRequired(getPublicLink)).Methods("POST") + BaseRoutes.Files.Handle("/get_export", ApiUserRequired(getExport)).Methods("GET") } func uploadFile(c *Context, w http.ResponseWriter, r *http.Request) { @@ -101,7 +100,7 @@ func uploadFile(c *Context, w http.ResponseWriter, r *http.Request) { return } - cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, channelId, c.Session.UserId) + cchan := Srv.Store.Channel().CheckPermissionsTo(c.TeamId, channelId, c.Session.UserId) files := m.File["files"] @@ -147,7 +146,7 @@ func uploadFile(c *Context, w http.ResponseWriter, r *http.Request) { } } - path := "teams/" + c.Session.TeamId + "/channels/" + channelId + "/users/" + c.Session.UserId + "/" + uid + "/" + filename + path := "teams/" + c.TeamId + "/channels/" + channelId + "/users/" + c.Session.UserId + "/" + uid + "/" + filename if err := WriteFile(buf.Bytes(), path); err != nil { c.Err = err @@ -164,7 +163,7 @@ func uploadFile(c *Context, w http.ResponseWriter, r *http.Request) { resStruct.ClientIds = append(resStruct.ClientIds, clientId) } - handleImagesAndForget(imageNameList, imageDataList, c.Session.TeamId, channelId, c.Session.UserId) + handleImagesAndForget(imageNameList, imageDataList, c.TeamId, channelId, c.Session.UserId) w.Write([]byte(resStruct.ToJson())) } @@ -319,9 +318,9 @@ func getFileInfo(c *Context, w http.ResponseWriter, r *http.Request) { return } - cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, channelId, c.Session.UserId) + cchan := Srv.Store.Channel().CheckPermissionsTo(c.TeamId, channelId, c.Session.UserId) - path := "teams/" + c.Session.TeamId + "/channels/" + channelId + "/users/" + userId + "/" + filename + path := "teams/" + c.TeamId + "/channels/" + channelId + "/users/" + userId + "/" + filename var info *model.FileInfo if cached, ok := fileInfoCache.Get(path); ok { @@ -380,13 +379,13 @@ func getFile(c *Context, w http.ResponseWriter, r *http.Request) { data := r.URL.Query().Get("d") teamId := r.URL.Query().Get("t") - cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, channelId, c.Session.UserId) + cchan := Srv.Store.Channel().CheckPermissionsTo(c.TeamId, channelId, c.Session.UserId) path := "" if len(teamId) == 26 { path = "teams/" + teamId + "/channels/" + channelId + "/users/" + userId + "/" + filename } else { - path = "teams/" + c.Session.TeamId + "/channels/" + channelId + "/users/" + userId + "/" + filename + path = "teams/" + c.TeamId + "/channels/" + channelId + "/users/" + userId + "/" + filename } fileData := make(chan []byte) @@ -460,6 +459,7 @@ func getPublicLink(c *Context, w http.ResponseWriter, r *http.Request) { if !utils.Cfg.FileSettings.EnablePublicLink { c.Err = model.NewLocAppError("getPublicLink", "api.file.get_public_link.disabled.app_error", nil, "") c.Err.StatusCode = http.StatusForbidden + return } props := model.MapFromJson(r.Body) @@ -480,7 +480,7 @@ func getPublicLink(c *Context, w http.ResponseWriter, r *http.Request) { userId := matches[0][2] filename = matches[0][3] - cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, channelId, c.Session.UserId) + cchan := Srv.Store.Channel().CheckPermissionsTo(c.TeamId, channelId, c.Session.UserId) newProps := make(map[string]string) newProps["filename"] = filename @@ -488,7 +488,7 @@ func getPublicLink(c *Context, w http.ResponseWriter, r *http.Request) { data := model.MapToJson(newProps) hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.FileSettings.PublicLinkSalt)) - url := fmt.Sprintf("%s/api/v1/files/get/%s/%s/%s?d=%s&h=%s&t=%s", c.GetSiteURL(), channelId, userId, filename, url.QueryEscape(data), url.QueryEscape(hash), c.Session.TeamId) + url := fmt.Sprintf("%s/files/get/%s/%s/%s?d=%s&h=%s&t=%s", c.GetSiteURL()+model.API_URL_SUFFIX, channelId, userId, filename, url.QueryEscape(data), url.QueryEscape(hash), c.TeamId) if !c.HasPermissionsToChannel(cchan, "getPublicLink") { return @@ -501,7 +501,7 @@ func getPublicLink(c *Context, w http.ResponseWriter, r *http.Request) { } func getExport(c *Context, w http.ResponseWriter, r *http.Request) { - if !c.HasPermissionsToTeam(c.Session.TeamId, "export") || !c.IsTeamAdmin() { + if !c.HasPermissionsToTeam(c.TeamId, "export") || !c.IsTeamAdmin() { c.Err = model.NewLocAppError("getExport", "api.file.get_export.team_admin.app_error", nil, "userId="+c.Session.UserId) c.Err.StatusCode = http.StatusForbidden return diff --git a/api/file_benchmark_test.go b/api/file_benchmark_test.go index a02bffa0e..d73097072 100644 --- a/api/file_benchmark_test.go +++ b/api/file_benchmark_test.go @@ -13,7 +13,9 @@ import ( ) func BenchmarkUploadFile(b *testing.B) { - _, _, channel := SetupBenchmark() + th := Setup().InitBasic() + Client := th.BasicClient + channel := th.BasicChannel testPoster := NewAutoPostCreator(Client, channel.Id) @@ -25,7 +27,10 @@ func BenchmarkUploadFile(b *testing.B) { } func BenchmarkGetFile(b *testing.B) { - team, _, channel := SetupBenchmark() + th := Setup().InitBasic() + Client := th.BasicClient + team := th.BasicTeam + channel := th.BasicChannel testPoster := NewAutoPostCreator(Client, channel.Id) filenames, err := testPoster.UploadTestFile() @@ -53,7 +58,9 @@ func BenchmarkGetFile(b *testing.B) { } func BenchmarkGetPublicLink(b *testing.B) { - _, _, channel := SetupBenchmark() + th := Setup().InitBasic() + Client := th.BasicClient + channel := th.BasicChannel testPoster := NewAutoPostCreator(Client, channel.Id) filenames, err := testPoster.UploadTestFile() diff --git a/api/file_test.go b/api/file_test.go index 3aa1a56f9..dd4a8520b 100644 --- a/api/file_test.go +++ b/api/file_test.go @@ -22,19 +22,11 @@ import ( ) func TestUploadFile(t *testing.T) { - Setup() - - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - - Client.LoginByEmail(team.Name, user1.Email, "pwd") - - channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} - channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) + th := Setup().InitBasic() + Client := th.BasicClient + team := th.BasicTeam + user := th.BasicUser + channel := th.BasicChannel body := &bytes.Buffer{} writer := multipart.NewWriter(body) @@ -45,6 +37,9 @@ func TestUploadFile(t *testing.T) { path := utils.FindDir("tests") file, err := os.Open(path + "/test.png") + if err != nil { + t.Fatal(err) + } defer file.Close() _, err = io.Copy(part, file) @@ -57,7 +52,7 @@ func TestUploadFile(t *testing.T) { t.Fatal(err) } - _, err = field.Write([]byte(channel1.Id)) + _, err = field.Write([]byte(channel.Id)) if err != nil { t.Fatal(err) } @@ -67,7 +62,7 @@ func TestUploadFile(t *testing.T) { t.Fatal(err) } - resp, appErr := Client.UploadFile("/files/upload", body.Bytes(), writer.FormDataContentType()) + resp, appErr := Client.UploadPostAttachment(body.Bytes(), writer.FormDataContentType()) if utils.Cfg.FileSettings.DriverName == model.IMAGE_DRIVER_S3 { if appErr != nil { t.Fatal(appErr) @@ -90,17 +85,17 @@ func TestUploadFile(t *testing.T) { // wait a bit for files to ready time.Sleep(5 * time.Second) - err = bucket.Del("teams/" + team.Id + "/channels/" + channel1.Id + "/users/" + user1.Id + "/" + filename) + err = bucket.Del("teams/" + team.Id + "/channels/" + channel.Id + "/users/" + user.Id + "/" + filename) if err != nil { t.Fatal(err) } - err = bucket.Del("teams/" + team.Id + "/channels/" + channel1.Id + "/users/" + user1.Id + "/" + fileId + "_thumb.jpg") + err = bucket.Del("teams/" + team.Id + "/channels/" + channel.Id + "/users/" + user.Id + "/" + fileId + "_thumb.jpg") if err != nil { t.Fatal(err) } - err = bucket.Del("teams/" + team.Id + "/channels/" + channel1.Id + "/users/" + user1.Id + "/" + fileId + "_preview.jpg") + err = bucket.Del("teams/" + team.Id + "/channels/" + channel.Id + "/users/" + user.Id + "/" + fileId + "_preview.jpg") if err != nil { t.Fatal(err) } @@ -115,17 +110,17 @@ func TestUploadFile(t *testing.T) { // wait a bit for files to ready time.Sleep(5 * time.Second) - path := utils.Cfg.FileSettings.Directory + "teams/" + team.Id + "/channels/" + channel1.Id + "/users/" + user1.Id + "/" + filename + path := utils.Cfg.FileSettings.Directory + "teams/" + team.Id + "/channels/" + channel.Id + "/users/" + user.Id + "/" + filename if err := os.Remove(path); err != nil { t.Fatal("Couldn't remove file at " + path) } - path = utils.Cfg.FileSettings.Directory + "teams/" + team.Id + "/channels/" + channel1.Id + "/users/" + user1.Id + "/" + fileId + "_thumb.jpg" + path = utils.Cfg.FileSettings.Directory + "teams/" + team.Id + "/channels/" + channel.Id + "/users/" + user.Id + "/" + fileId + "_thumb.jpg" if err := os.Remove(path); err != nil { t.Fatal("Couldn't remove file at " + path) } - path = utils.Cfg.FileSettings.Directory + "teams/" + team.Id + "/channels/" + channel1.Id + "/users/" + user1.Id + "/" + fileId + "_preview.jpg" + path = utils.Cfg.FileSettings.Directory + "teams/" + team.Id + "/channels/" + channel.Id + "/users/" + user.Id + "/" + fileId + "_preview.jpg" if err := os.Remove(path); err != nil { t.Fatal("Couldn't remove file at " + path) } @@ -137,25 +132,18 @@ func TestUploadFile(t *testing.T) { } func TestGetFile(t *testing.T) { - Setup() + th := Setup().InitBasic() + Client := th.BasicClient + team := th.BasicTeam + user := th.BasicUser + channel := th.BasicChannel + enablePublicLink := utils.Cfg.FileSettings.EnablePublicLink defer func() { utils.Cfg.FileSettings.EnablePublicLink = enablePublicLink }() utils.Cfg.FileSettings.EnablePublicLink = true - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - - Client.LoginByEmail(team.Name, user1.Email, "pwd") - - channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} - channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) - if utils.Cfg.FileSettings.DriverName != "" { body := &bytes.Buffer{} writer := multipart.NewWriter(body) @@ -181,7 +169,7 @@ func TestGetFile(t *testing.T) { t.Fatal(err) } - _, err = field.Write([]byte(channel1.Id)) + _, err = field.Write([]byte(channel.Id)) if err != nil { t.Fatal(err) } @@ -191,7 +179,7 @@ func TestGetFile(t *testing.T) { t.Fatal(err) } - resp, upErr := Client.UploadFile("/files/upload", body.Bytes(), writer.FormDataContentType()) + resp, upErr := Client.UploadPostAttachment(body.Bytes(), writer.FormDataContentType()) if upErr != nil { t.Fatal(upErr) } @@ -217,8 +205,9 @@ func TestGetFile(t *testing.T) { team2 := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team2 = Client.Must(Client.CreateTeam(team2)).Data.(*model.Team) - user2 := &model.User{TeamId: team2.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} + user2 := &model.User{Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) + LinkUserToTeam(user2, team2) store.Must(Srv.Store.User().VerifyEmail(user2.Id)) newProps := make(map[string]string) @@ -229,6 +218,7 @@ func TestGetFile(t *testing.T) { hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.FileSettings.PublicLinkSalt)) Client.LoginByEmail(team2.Name, user2.Email, "pwd") + Client.SetTeamId(team2.Id) if _, downErr := Client.GetFile(filenames[0]+"?d="+url.QueryEscape(data)+"&h="+url.QueryEscape(hash)+"&t="+team.Id, false); downErr != nil { t.Fatal(downErr) @@ -278,17 +268,17 @@ func TestGetFile(t *testing.T) { filename := filenames[len(filenames)-2] + "/" + filenames[len(filenames)-1] fileId := strings.Split(filename, ".")[0] - err = bucket.Del("teams/" + team.Id + "/channels/" + channel1.Id + "/users/" + user1.Id + "/" + filename) + err = bucket.Del("teams/" + team.Id + "/channels/" + channel.Id + "/users/" + user.Id + "/" + filename) if err != nil { t.Fatal(err) } - err = bucket.Del("teams/" + team.Id + "/channels/" + channel1.Id + "/users/" + user1.Id + "/" + fileId + "_thumb.jpg") + err = bucket.Del("teams/" + team.Id + "/channels/" + channel.Id + "/users/" + user.Id + "/" + fileId + "_thumb.jpg") if err != nil { t.Fatal(err) } - err = bucket.Del("teams/" + team.Id + "/channels/" + channel1.Id + "/users/" + user1.Id + "/" + fileId + "_preview.jpg") + err = bucket.Del("teams/" + team.Id + "/channels/" + channel.Id + "/users/" + user.Id + "/" + fileId + "_preview.jpg") if err != nil { t.Fatal(err) } @@ -297,17 +287,17 @@ func TestGetFile(t *testing.T) { filename := filenames[len(filenames)-2] + "/" + filenames[len(filenames)-1] fileId := strings.Split(filename, ".")[0] - path := utils.Cfg.FileSettings.Directory + "teams/" + team.Id + "/channels/" + channel1.Id + "/users/" + user1.Id + "/" + filename + path := utils.Cfg.FileSettings.Directory + "teams/" + team.Id + "/channels/" + channel.Id + "/users/" + user.Id + "/" + filename if err := os.Remove(path); err != nil { t.Fatal("Couldn't remove file at " + path) } - path = utils.Cfg.FileSettings.Directory + "teams/" + team.Id + "/channels/" + channel1.Id + "/users/" + user1.Id + "/" + fileId + "_thumb.jpg" + path = utils.Cfg.FileSettings.Directory + "teams/" + team.Id + "/channels/" + channel.Id + "/users/" + user.Id + "/" + fileId + "_thumb.jpg" if err := os.Remove(path); err != nil { t.Fatal("Couldn't remove file at " + path) } - path = utils.Cfg.FileSettings.Directory + "teams/" + team.Id + "/channels/" + channel1.Id + "/users/" + user1.Id + "/" + fileId + "_preview.jpg" + path = utils.Cfg.FileSettings.Directory + "teams/" + team.Id + "/channels/" + channel.Id + "/users/" + user.Id + "/" + fileId + "_preview.jpg" if err := os.Remove(path); err != nil { t.Fatal("Couldn't remove file at " + path) } @@ -320,25 +310,18 @@ func TestGetFile(t *testing.T) { } func TestGetPublicLink(t *testing.T) { - Setup() - - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - - user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user2.Id)) - - Client.LoginByEmail(team.Name, user1.Email, "pwd") - - channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} - channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) + th := Setup().InitBasic() + Client := th.BasicClient + team := th.BasicTeam + user := th.BasicUser + channel := th.BasicChannel if utils.Cfg.FileSettings.DriverName != "" { + enablePublicLink := utils.Cfg.FileSettings.EnablePublicLink + defer func() { + utils.Cfg.FileSettings.EnablePublicLink = enablePublicLink + }() + utils.Cfg.FileSettings.EnablePublicLink = true body := &bytes.Buffer{} writer := multipart.NewWriter(body) @@ -364,7 +347,7 @@ func TestGetPublicLink(t *testing.T) { t.Fatal(err) } - _, err = field.Write([]byte(channel1.Id)) + _, err = field.Write([]byte(channel.Id)) if err != nil { t.Fatal(err) } @@ -374,14 +357,14 @@ func TestGetPublicLink(t *testing.T) { t.Fatal(err) } - resp, upErr := Client.UploadFile("/files/upload", body.Bytes(), writer.FormDataContentType()) + resp, upErr := Client.UploadPostAttachment(body.Bytes(), writer.FormDataContentType()) if upErr != nil { t.Fatal(upErr) } filenames := resp.Data.(*model.FileUploadResponse).Filenames - post1 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a", Filenames: filenames} + post1 := &model.Post{ChannelId: channel.Id, Message: "a" + model.NewId() + "a", Filenames: filenames} rpost1, postErr := Client.CreatePost(post1) if postErr != nil { @@ -408,7 +391,8 @@ func TestGetPublicLink(t *testing.T) { t.Fatal("Should have errored - bad file path") } - Client.LoginByEmail(team.Name, user2.Email, "pwd") + th.LoginBasic2() + data["filename"] = filenames[0] if _, err := Client.GetPublicLink(data); err == nil { t.Fatal("should have errored, user not member of channel") @@ -427,17 +411,17 @@ func TestGetPublicLink(t *testing.T) { filename := filenames[len(filenames)-2] + "/" + filenames[len(filenames)-1] fileId := strings.Split(filename, ".")[0] - err = bucket.Del("teams/" + team.Id + "/channels/" + channel1.Id + "/users/" + user1.Id + "/" + filename) + err = bucket.Del("teams/" + team.Id + "/channels/" + channel.Id + "/users/" + user.Id + "/" + filename) if err != nil { t.Fatal(err) } - err = bucket.Del("teams/" + team.Id + "/channels/" + channel1.Id + "/users/" + user1.Id + "/" + fileId + "_thumb.jpg") + err = bucket.Del("teams/" + team.Id + "/channels/" + channel.Id + "/users/" + user.Id + "/" + fileId + "_thumb.jpg") if err != nil { t.Fatal(err) } - err = bucket.Del("teams/" + team.Id + "/channels/" + channel1.Id + "/users/" + user1.Id + "/" + fileId + "_preview.jpg") + err = bucket.Del("teams/" + team.Id + "/channels/" + channel.Id + "/users/" + user.Id + "/" + fileId + "_preview.jpg") if err != nil { t.Fatal(err) } @@ -446,17 +430,17 @@ func TestGetPublicLink(t *testing.T) { filename := filenames[len(filenames)-2] + "/" + filenames[len(filenames)-1] fileId := strings.Split(filename, ".")[0] - path := utils.Cfg.FileSettings.Directory + "teams/" + team.Id + "/channels/" + channel1.Id + "/users/" + user1.Id + "/" + filename + path := utils.Cfg.FileSettings.Directory + "teams/" + team.Id + "/channels/" + channel.Id + "/users/" + user.Id + "/" + filename if err := os.Remove(path); err != nil { t.Fatal("Couldn't remove file at " + path) } - path = utils.Cfg.FileSettings.Directory + "teams/" + team.Id + "/channels/" + channel1.Id + "/users/" + user1.Id + "/" + fileId + "_thumb.jpg" + path = utils.Cfg.FileSettings.Directory + "teams/" + team.Id + "/channels/" + channel.Id + "/users/" + user.Id + "/" + fileId + "_thumb.jpg" if err := os.Remove(path); err != nil { t.Fatal("Couldn't remove file at " + path) } - path = utils.Cfg.FileSettings.Directory + "teams/" + team.Id + "/channels/" + channel1.Id + "/users/" + user1.Id + "/" + fileId + "_preview.jpg" + path = utils.Cfg.FileSettings.Directory + "teams/" + team.Id + "/channels/" + channel.Id + "/users/" + user.Id + "/" + fileId + "_preview.jpg" if err := os.Remove(path); err != nil { t.Fatal("Couldn't remove file at " + path) } diff --git a/api/import.go b/api/import.go index 7590277b0..c39ec5220 100644 --- a/api/import.go +++ b/api/import.go @@ -22,7 +22,7 @@ func ImportPost(post *model.Post) { } } -func ImportUser(user *model.User) *model.User { +func ImportUser(teamId string, user *model.User) *model.User { user.MakeNonNil() if result := <-Srv.Store.User().Save(user); result.Err != nil { @@ -31,8 +31,8 @@ func ImportUser(user *model.User) *model.User { } else { ruser := result.Data.(*model.User) - if err := JoinDefaultChannels(ruser, ""); err != nil { - l4g.Error(utils.T("api.import.import_user.joining_default.error"), ruser.Id, ruser.TeamId, err) + if err := JoinDefaultChannels(teamId, ruser, ""); err != nil { + l4g.Error(utils.T("api.import.import_user.joining_default.error"), ruser.Id, teamId, err) } if cresult := <-Srv.Store.User().VerifyEmail(ruser.Id); cresult.Err != nil { diff --git a/api/license.go b/api/license.go index 4bf8cd3b8..1dbb2b281 100644 --- a/api/license.go +++ b/api/license.go @@ -6,7 +6,6 @@ package api import ( "bytes" l4g "github.com/alecthomas/log4go" - "github.com/gorilla/mux" "github.com/mattermost/platform/model" "github.com/mattermost/platform/utils" "io" @@ -19,13 +18,12 @@ const ( INVALID_LICENSE_ERROR = "api.license.add_license.invalid.app_error" ) -func InitLicense(r *mux.Router) { +func InitLicense() { l4g.Debug(utils.T("api.license.init.debug")) - sr := r.PathPrefix("/license").Subrouter() - sr.Handle("/add", ApiAdminSystemRequired(addLicense)).Methods("POST") - sr.Handle("/remove", ApiAdminSystemRequired(removeLicense)).Methods("POST") - sr.Handle("/client_config", ApiAppHandler(getClientLicenceConfig)).Methods("GET") + BaseRoutes.License.Handle("/add", ApiAdminSystemRequired(addLicense)).Methods("POST") + BaseRoutes.License.Handle("/remove", ApiAdminSystemRequired(removeLicense)).Methods("POST") + BaseRoutes.License.Handle("/client_config", ApiAppHandler(getClientLicenceConfig)).Methods("GET") } func LoadLicense() { diff --git a/api/license_test.go b/api/license_test.go index 0126d6e54..c5fffd6e9 100644 --- a/api/license_test.go +++ b/api/license_test.go @@ -9,7 +9,8 @@ import ( ) func TestGetLicenceConfig(t *testing.T) { - Setup() + th := Setup().InitBasic() + Client := th.BasicClient if result, err := Client.GetClientLicenceConfig(""); err != nil { t.Fatal(err) diff --git a/api/oauth.go b/api/oauth.go index a7119d7e5..0375f4e6f 100644 --- a/api/oauth.go +++ b/api/oauth.go @@ -4,7 +4,10 @@ package api import ( + "crypto/tls" + b64 "encoding/base64" "fmt" + "io" "net/http" "net/url" "strconv" @@ -12,31 +15,29 @@ import ( l4g "github.com/alecthomas/log4go" "github.com/gorilla/mux" + "github.com/mattermost/platform/einterfaces" "github.com/mattermost/platform/model" "github.com/mattermost/platform/utils" ) -func InitOAuth(r *mux.Router) { +func InitOAuth() { l4g.Debug(utils.T("api.oauth.init.debug")) - sr := r.PathPrefix("/oauth").Subrouter() + BaseRoutes.OAuth.Handle("/register", ApiUserRequired(registerOAuthApp)).Methods("POST") + BaseRoutes.OAuth.Handle("/allow", ApiUserRequired(allowOAuth)).Methods("GET") + BaseRoutes.OAuth.Handle("/{service:[A-Za-z]+}/complete", AppHandlerIndependent(completeOAuth)).Methods("GET") + BaseRoutes.OAuth.Handle("/{service:[A-Za-z]+}/login", AppHandlerIndependent(loginWithOAuth)).Methods("GET") + BaseRoutes.OAuth.Handle("/{service:[A-Za-z]+}/signup", AppHandlerIndependent(signupWithOAuth)).Methods("GET") + BaseRoutes.OAuth.Handle("/authorize", ApiUserRequired(authorizeOAuth)).Methods("GET") + BaseRoutes.OAuth.Handle("/access_token", ApiAppHandler(getAccessToken)).Methods("POST") - sr.Handle("/register", ApiUserRequired(registerOAuthApp)).Methods("POST") - sr.Handle("/allow", ApiUserRequired(allowOAuth)).Methods("GET") - sr.Handle("/{service:[A-Za-z]+}/complete", AppHandlerIndependent(completeOAuth)).Methods("GET") - sr.Handle("/{service:[A-Za-z]+}/login", AppHandlerIndependent(loginWithOAuth)).Methods("GET") - sr.Handle("/{service:[A-Za-z]+}/signup", AppHandlerIndependent(signupWithOAuth)).Methods("GET") - sr.Handle("/authorize", ApiUserRequired(authorizeOAuth)).Methods("GET") - sr.Handle("/access_token", ApiAppHandler(getAccessToken)).Methods("POST") - - mr := Srv.Router - mr.Handle("/authorize", ApiUserRequired(authorizeOAuth)).Methods("GET") - mr.Handle("/access_token", ApiAppHandler(getAccessToken)).Methods("POST") + BaseRoutes.Root.Handle("/authorize", ApiUserRequired(authorizeOAuth)).Methods("GET") + BaseRoutes.Root.Handle("/access_token", ApiAppHandler(getAccessToken)).Methods("POST") // Handle all the old routes, to be later removed - mr.Handle("/{service:[A-Za-z]+}/complete", AppHandlerIndependent(completeOAuth)).Methods("GET") - mr.Handle("/signup/{service:[A-Za-z]+}/complete", AppHandlerIndependent(completeOAuth)).Methods("GET") - mr.Handle("/login/{service:[A-Za-z]+}/complete", AppHandlerIndependent(completeOAuth)).Methods("GET") + BaseRoutes.Root.Handle("/{service:[A-Za-z]+}/complete", AppHandlerIndependent(completeOAuth)).Methods("GET") + BaseRoutes.Root.Handle("/signup/{service:[A-Za-z]+}/complete", AppHandlerIndependent(completeOAuth)).Methods("GET") + BaseRoutes.Root.Handle("/login/{service:[A-Za-z]+}/complete", AppHandlerIndependent(completeOAuth)).Methods("GET") } func registerOAuthApp(c *Context, w http.ResponseWriter, r *http.Request) { @@ -190,40 +191,40 @@ func completeOAuth(c *Context, w http.ResponseWriter, r *http.Request) { uri := c.GetSiteURL() + "/signup/" + service + "/complete" - if body, team, props, err := AuthorizeOAuthUser(service, code, state, uri); err != nil { + if body, teamId, props, err := AuthorizeOAuthUser(service, code, state, uri); err != nil { c.Err = err return } else { action := props["action"] switch action { case model.OAUTH_ACTION_SIGNUP: - CreateOAuthUser(c, w, r, service, body, team) + CreateOAuthUser(c, w, r, service, body, teamId) if c.Err == nil { - http.Redirect(w, r, GetProtocol(r)+"://"+r.Host+"/"+team.Name, http.StatusTemporaryRedirect) + http.Redirect(w, r, GetProtocol(r)+"://"+r.Host, http.StatusTemporaryRedirect) } break case model.OAUTH_ACTION_LOGIN: - LoginByOAuth(c, w, r, service, body, team) + LoginByOAuth(c, w, r, service, body) if c.Err == nil { - http.Redirect(w, r, GetProtocol(r)+"://"+r.Host+"/"+team.Name, http.StatusTemporaryRedirect) + http.Redirect(w, r, GetProtocol(r)+"://"+r.Host, http.StatusTemporaryRedirect) } break case model.OAUTH_ACTION_EMAIL_TO_SSO: - CompleteSwitchWithOAuth(c, w, r, service, body, team, props["email"]) + CompleteSwitchWithOAuth(c, w, r, service, body, props["email"]) if c.Err == nil { - http.Redirect(w, r, GetProtocol(r)+"://"+r.Host+"/"+team.Name+"/login?extra=signin_change", http.StatusTemporaryRedirect) + http.Redirect(w, r, GetProtocol(r)+"://"+r.Host+"/login?extra=signin_change", http.StatusTemporaryRedirect) } break case model.OAUTH_ACTION_SSO_TO_EMAIL: - LoginByOAuth(c, w, r, service, body, team) + LoginByOAuth(c, w, r, service, body) if c.Err == nil { - http.Redirect(w, r, GetProtocol(r)+"://"+r.Host+"/"+team.Name+"/"+"/claim?email="+url.QueryEscape(props["email"]), http.StatusTemporaryRedirect) + http.Redirect(w, r, GetProtocol(r)+"://"+r.Host+"/claim?email="+url.QueryEscape(props["email"]), http.StatusTemporaryRedirect) } break default: - LoginByOAuth(c, w, r, service, body, team) + LoginByOAuth(c, w, r, service, body) if c.Err == nil { - http.Redirect(w, r, GetProtocol(r)+"://"+r.Host+"/"+team.Name, http.StatusTemporaryRedirect) + http.Redirect(w, r, GetProtocol(r)+"://"+r.Host, http.StatusTemporaryRedirect) } break } @@ -257,7 +258,7 @@ func authorizeOAuth(c *Context, w http.ResponseWriter, r *http.Request) { } var team *model.Team - if result := <-Srv.Store.Team().Get(c.Session.TeamId); result.Err != nil { + if result := <-Srv.Store.Team().Get(c.TeamId); result.Err != nil { c.Err = result.Err return } else { @@ -389,7 +390,7 @@ func getAccessToken(c *Context, w http.ResponseWriter, r *http.Request) { user = result.Data.(*model.User) } - session := &model.Session{UserId: user.Id, TeamId: user.TeamId, Roles: user.Roles, IsOAuth: true} + session := &model.Session{UserId: user.Id, Roles: user.Roles, IsOAuth: true} if result := <-Srv.Store.Session().Save(session); result.Err != nil { c.Err = model.NewLocAppError("getAccessToken", "web.get_access_token.internal_session.app_error", nil, "") @@ -422,24 +423,11 @@ func loginWithOAuth(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) service := params["service"] loginHint := r.URL.Query().Get("login_hint") - teamName := r.URL.Query().Get("team") - - if len(teamName) == 0 { - c.Err = model.NewLocAppError("loginWithOAuth", "web.login_with_oauth.invalid_team.app_error", nil, "team_name="+teamName) - c.Err.StatusCode = http.StatusBadRequest - return - } - - // Make sure team exists - if result := <-Srv.Store.Team().GetByName(teamName); result.Err != nil { - c.Err = result.Err - return - } stateProps := map[string]string{} stateProps["action"] = model.OAUTH_ACTION_LOGIN - if authUrl, err := GetAuthorizationCode(c, service, teamName, stateProps, loginHint); err != nil { + if authUrl, err := GetAuthorizationCode(c, service, stateProps, loginHint); err != nil { c.Err = err return } else { @@ -450,31 +438,19 @@ func loginWithOAuth(c *Context, w http.ResponseWriter, r *http.Request) { func signupWithOAuth(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) service := params["service"] - teamName := r.URL.Query().Get("team") if !utils.Cfg.TeamSettings.EnableUserCreation { - c.Err = model.NewLocAppError("signupTeam", "web.singup_with_oauth.disabled.app_error", nil, "") + c.Err = model.NewLocAppError("signupWithOAuth", "web.singup_with_oauth.disabled.app_error", nil, "") c.Err.StatusCode = http.StatusNotImplemented return } - if len(teamName) == 0 { - c.Err = model.NewLocAppError("signupWithOAuth", "web.singup_with_oauth.invalid_team.app_error", nil, "team_name="+teamName) - c.Err.StatusCode = http.StatusBadRequest - return - } - hash := r.URL.Query().Get("h") - var team *model.Team - if result := <-Srv.Store.Team().GetByName(teamName); result.Err != nil { - c.Err = result.Err - return - } else { - team = result.Data.(*model.Team) - } + teamId := "" + inviteId := r.URL.Query().Get("id") - if IsVerifyHashRequired(nil, team, hash) { + if len(hash) > 0 { data := r.URL.Query().Get("d") props := model.MapFromJson(strings.NewReader(data)) @@ -489,19 +465,173 @@ func signupWithOAuth(c *Context, w http.ResponseWriter, r *http.Request) { return } - if team.Id != props["id"] { - c.Err = model.NewLocAppError("signupWithOAuth", "web.singup_with_oauth.invalid_team.app_error", nil, data) - return + teamId = props["id"] + } else if len(inviteId) != 0 { + if result := <-Srv.Store.Team().GetByInviteId(inviteId); result.Err != nil { + // soft fail, so we still create user but don't auto-join team + l4g.Error("%v", result.Err) + } else { + teamId = result.Data.(*model.Team).Id } } stateProps := map[string]string{} stateProps["action"] = model.OAUTH_ACTION_SIGNUP + if len(teamId) != 0 { + stateProps["team_id"] = teamId + } - if authUrl, err := GetAuthorizationCode(c, service, teamName, stateProps, ""); err != nil { + if authUrl, err := GetAuthorizationCode(c, service, stateProps, ""); err != nil { c.Err = err return } else { http.Redirect(w, r, authUrl, http.StatusFound) } } + +func GetAuthorizationCode(c *Context, service string, props map[string]string, loginHint string) (string, *model.AppError) { + + sso := utils.Cfg.GetSSOService(service) + if sso != nil && !sso.Enable { + return "", model.NewLocAppError("GetAuthorizationCode", "api.user.get_authorization_code.unsupported.app_error", nil, "service="+service) + } + + clientId := sso.Id + endpoint := sso.AuthEndpoint + scope := sso.Scope + + props["hash"] = model.HashPassword(clientId) + state := b64.StdEncoding.EncodeToString([]byte(model.MapToJson(props))) + + redirectUri := c.GetSiteURL() + "/signup/" + service + "/complete" + + authUrl := endpoint + "?response_type=code&client_id=" + clientId + "&redirect_uri=" + url.QueryEscape(redirectUri) + "&state=" + url.QueryEscape(state) + + if len(scope) > 0 { + authUrl += "&scope=" + utils.UrlEncode(scope) + } + + if len(loginHint) > 0 { + authUrl += "&login_hint=" + utils.UrlEncode(loginHint) + } + + return authUrl, nil +} + +func AuthorizeOAuthUser(service, code, state, redirectUri string) (io.ReadCloser, string, map[string]string, *model.AppError) { + sso := utils.Cfg.GetSSOService(service) + if sso == nil || !sso.Enable { + return nil, "", nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.unsupported.app_error", nil, "service="+service) + } + + stateStr := "" + if b, err := b64.StdEncoding.DecodeString(state); err != nil { + return nil, "", nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.invalid_state.app_error", nil, err.Error()) + } else { + stateStr = string(b) + } + + stateProps := model.MapFromJson(strings.NewReader(stateStr)) + + if !model.ComparePassword(stateProps["hash"], sso.Id) { + return nil, "", nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.invalid_state.app_error", nil, "") + } + + teamId := stateProps["team_id"] + + p := url.Values{} + p.Set("client_id", sso.Id) + p.Set("client_secret", sso.Secret) + p.Set("code", code) + p.Set("grant_type", model.ACCESS_TOKEN_GRANT_TYPE) + p.Set("redirect_uri", redirectUri) + + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: *utils.Cfg.ServiceSettings.EnableInsecureOutgoingConnections}, + } + client := &http.Client{Transport: tr} + req, _ := http.NewRequest("POST", sso.TokenEndpoint, strings.NewReader(p.Encode())) + + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + req.Header.Set("Accept", "application/json") + + var ar *model.AccessResponse + if resp, err := client.Do(req); err != nil { + return nil, "", nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.token_failed.app_error", nil, err.Error()) + } else { + ar = model.AccessResponseFromJson(resp.Body) + if ar == nil { + return nil, "", nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.bad_response.app_error", nil, "") + } + } + + if strings.ToLower(ar.TokenType) != model.ACCESS_TOKEN_TYPE { + return nil, "", nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.bad_token.app_error", nil, "token_type="+ar.TokenType) + } + + if len(ar.AccessToken) == 0 { + return nil, "", nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.missing.app_error", nil, "") + } + + p = url.Values{} + p.Set("access_token", ar.AccessToken) + req, _ = http.NewRequest("GET", sso.UserApiEndpoint, strings.NewReader("")) + + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + req.Header.Set("Accept", "application/json") + req.Header.Set("Authorization", "Bearer "+ar.AccessToken) + + if resp, err := client.Do(req); err != nil { + return nil, "", nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.service.app_error", + map[string]interface{}{"Service": service}, err.Error()) + } else { + return resp.Body, teamId, stateProps, nil + } + +} + +func CompleteSwitchWithOAuth(c *Context, w http.ResponseWriter, r *http.Request, service string, userData io.ReadCloser, email string) { + authData := "" + ssoEmail := "" + provider := einterfaces.GetOauthProvider(service) + if provider == nil { + c.Err = model.NewLocAppError("CompleteClaimWithOAuth", "api.user.complete_switch_with_oauth.unavailable.app_error", + map[string]interface{}{"Service": service}, "") + return + } else { + ssoUser := provider.GetUserFromJson(userData) + authData = ssoUser.AuthData + ssoEmail = ssoUser.Email + } + + if len(authData) == 0 { + c.Err = model.NewLocAppError("CompleteClaimWithOAuth", "api.user.complete_switch_with_oauth.parse.app_error", + map[string]interface{}{"Service": service}, "") + return + } + + if len(email) == 0 { + c.Err = model.NewLocAppError("CompleteClaimWithOAuth", "api.user.complete_switch_with_oauth.blank_email.app_error", nil, "") + return + } + + var user *model.User + if result := <-Srv.Store.User().GetByEmail(email); result.Err != nil { + c.Err = result.Err + return + } else { + user = result.Data.(*model.User) + } + + RevokeAllSession(c, user.Id) + if c.Err != nil { + return + } + + if result := <-Srv.Store.User().UpdateAuthData(user.Id, service, authData, ssoEmail); result.Err != nil { + c.Err = result.Err + return + } + + sendSignInChangeEmailAndForget(c, user.Email, c.GetSiteURL(), strings.Title(service)+" SSO") +} diff --git a/api/oauth_test.go b/api/oauth_test.go index 57772ccc5..aa3c025a7 100644 --- a/api/oauth_test.go +++ b/api/oauth_test.go @@ -5,22 +5,14 @@ package api import ( "github.com/mattermost/platform/model" - "github.com/mattermost/platform/store" "github.com/mattermost/platform/utils" "net/url" - "strings" "testing" ) func TestRegisterApp(t *testing.T) { - Setup() - - team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - rteam, _ := Client.CreateTeam(&team) - - user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Password: "pwd"} - ruser := Client.Must(Client.CreateUser(&user, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(ruser.Id)) + th := Setup().InitBasic() + Client := th.BasicClient app := &model.OAuthApp{Name: "TestApp" + model.NewId(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}} @@ -38,7 +30,7 @@ func TestRegisterApp(t *testing.T) { t.Fatal("not logged in - should have failed") } - Client.Must(Client.LoginById(ruser.Id, "pwd")) + th.LoginBasic() if result, err := Client.RegisterApp(app); err != nil { t.Fatal(err) @@ -70,19 +62,11 @@ func TestRegisterApp(t *testing.T) { } func TestAllowOAuth(t *testing.T) { - Setup() - - team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - rteam, _ := Client.CreateTeam(&team) - - user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Password: "pwd"} - ruser := Client.Must(Client.CreateUser(&user, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(ruser.Id)) + th := Setup().InitBasic() + Client := th.BasicClient app := &model.OAuthApp{Name: "TestApp" + model.NewId(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}} - Client.Must(Client.LoginById(ruser.Id, "pwd")) - state := "123" if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider { diff --git a/api/post.go b/api/post.go index 6f88c815b..cbfbf49f2 100644 --- a/api/post.go +++ b/api/post.go @@ -22,21 +22,21 @@ import ( "time" ) -func InitPost(r *mux.Router) { +func InitPost() { l4g.Debug(utils.T("api.post.init.debug")) - r.Handle("/posts/search", ApiUserRequired(searchPosts)).Methods("GET") - r.Handle("/posts/{post_id}", ApiUserRequired(getPostById)).Methods("GET") - - sr := r.PathPrefix("/channels/{id:[A-Za-z0-9]+}").Subrouter() - sr.Handle("/create", ApiUserRequired(createPost)).Methods("POST") - sr.Handle("/update", ApiUserRequired(updatePost)).Methods("POST") - sr.Handle("/posts/{offset:[0-9]+}/{limit:[0-9]+}", ApiUserRequiredActivity(getPosts, false)).Methods("GET") - sr.Handle("/posts/{time:[0-9]+}", ApiUserRequiredActivity(getPostsSince, false)).Methods("GET") - sr.Handle("/post/{post_id:[A-Za-z0-9]+}", ApiUserRequired(getPost)).Methods("GET") - sr.Handle("/post/{post_id:[A-Za-z0-9]+}/delete", ApiUserRequired(deletePost)).Methods("POST") - sr.Handle("/post/{post_id:[A-Za-z0-9]+}/before/{offset:[0-9]+}/{num_posts:[0-9]+}", ApiUserRequired(getPostsBefore)).Methods("GET") - sr.Handle("/post/{post_id:[A-Za-z0-9]+}/after/{offset:[0-9]+}/{num_posts:[0-9]+}", ApiUserRequired(getPostsAfter)).Methods("GET") + BaseRoutes.NeedTeam.Handle("/posts/search", ApiUserRequired(searchPosts)).Methods("GET") + BaseRoutes.NeedTeam.Handle("/posts/{post_id}", ApiUserRequired(getPostById)).Methods("GET") + + BaseRoutes.Posts.Handle("/create", ApiUserRequired(createPost)).Methods("POST") + BaseRoutes.Posts.Handle("/update", ApiUserRequired(updatePost)).Methods("POST") + BaseRoutes.Posts.Handle("/page/{offset:[0-9]+}/{limit:[0-9]+}", ApiUserRequiredActivity(getPosts, false)).Methods("GET") + BaseRoutes.Posts.Handle("/since/{time:[0-9]+}", ApiUserRequiredActivity(getPostsSince, false)).Methods("GET") + + BaseRoutes.NeedPost.Handle("/get", ApiUserRequired(getPost)).Methods("GET") + BaseRoutes.NeedPost.Handle("/delete", ApiUserRequired(deletePost)).Methods("POST") + BaseRoutes.NeedPost.Handle("/before/{offset:[0-9]+}/{num_posts:[0-9]+}", ApiUserRequired(getPostsBefore)).Methods("GET") + BaseRoutes.NeedPost.Handle("/after/{offset:[0-9]+}/{num_posts:[0-9]+}", ApiUserRequired(getPostsAfter)).Methods("GET") } func createPost(c *Context, w http.ResponseWriter, r *http.Request) { @@ -47,7 +47,7 @@ func createPost(c *Context, w http.ResponseWriter, r *http.Request) { } // Create and save post object to channel - cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, post.ChannelId, c.Session.UserId) + cchan := Srv.Store.Channel().CheckPermissionsTo(c.TeamId, post.ChannelId, c.Session.UserId) if !c.HasPermissionsToChannel(cchan, "createPost") { return @@ -228,15 +228,16 @@ func CreateWebhookPost(c *Context, channelId, text, overrideUsername, overrideIc func handlePostEventsAndForget(c *Context, post *model.Post, triggerWebhooks bool) { go func() { - tchan := Srv.Store.Team().Get(c.Session.TeamId) + tchan := Srv.Store.Team().Get(c.TeamId) cchan := Srv.Store.Channel().Get(post.ChannelId) uchan := Srv.Store.User().Get(post.UserId) - pchan := Srv.Store.User().GetProfiles(c.Session.TeamId) + pchan := Srv.Store.User().GetProfiles(c.TeamId) + dpchan := Srv.Store.User().GetDirectProfiles(c.Session.UserId) mchan := Srv.Store.Channel().GetMembers(post.ChannelId) var team *model.Team if result := <-tchan; result.Err != nil { - l4g.Error(utils.T("api.post.handle_post_events_and_forget.team.error"), c.Session.TeamId, result.Err) + l4g.Error(utils.T("api.post.handle_post_events_and_forget.team.error"), c.TeamId, result.Err) return } else { team = result.Data.(*model.Team) @@ -252,12 +253,22 @@ func handlePostEventsAndForget(c *Context, post *model.Post, triggerWebhooks boo var profiles map[string]*model.User if result := <-pchan; result.Err != nil { - l4g.Error(utils.T("api.post.handle_post_events_and_forget.profiles.error"), c.Session.TeamId, result.Err) + l4g.Error(utils.T("api.post.handle_post_events_and_forget.profiles.error"), c.TeamId, result.Err) return } else { profiles = result.Data.(map[string]*model.User) } + if result := <-dpchan; result.Err != nil { + l4g.Error(utils.T("api.post.handle_post_events_and_forget.profiles.error"), c.TeamId, result.Err) + return + } else { + dps := result.Data.(map[string]*model.User) + for k, v := range dps { + profiles[k] = v + } + } + var members []model.ChannelMember if result := <-mchan; result.Err != nil { l4g.Error(utils.T("api.post.handle_post_events_and_forget.members.error"), post.ChannelId, result.Err) @@ -282,7 +293,7 @@ func handlePostEventsAndForget(c *Context, post *model.Post, triggerWebhooks boo } if channel.Type == model.CHANNEL_DIRECT { - go makeDirectChannelVisible(c.Session.TeamId, post.ChannelId) + go makeDirectChannelVisible(c.TeamId, post.ChannelId) } }() } @@ -352,7 +363,7 @@ func handleWebhookEventsAndForget(c *Context, post *model.Post, team *model.Team return } - hchan := Srv.Store.Webhook().GetOutgoingByTeam(c.Session.TeamId) + hchan := Srv.Store.Webhook().GetOutgoingByTeam(c.TeamId) hooks := []*model.OutgoingWebhook{} @@ -416,8 +427,25 @@ func handleWebhookEventsAndForget(c *Context, post *model.Post, team *model.Team respProps := model.MapFromJson(resp.Body) // copy the context and create a mock session for posting the message - mockSession := model.Session{UserId: hook.CreatorId, TeamId: hook.TeamId, IsOAuth: false} - newContext := &Context{mockSession, model.NewId(), "", c.Path, nil, c.teamURLValid, c.teamURL, c.siteURL, c.T, c.Locale} + mockSession := model.Session{ + UserId: hook.CreatorId, + TeamMembers: []*model.TeamMember{{TeamId: hook.TeamId, UserId: hook.CreatorId}}, + IsOAuth: false, + } + + newContext := &Context{ + Session: mockSession, + RequestId: model.NewId(), + IpAddress: "", + Path: c.Path, + Err: nil, + teamURLValid: c.teamURLValid, + teamURL: c.teamURL, + siteURL: c.siteURL, + T: c.T, + Locale: c.Locale, + TeamId: hook.TeamId, + } if text, ok := respProps["text"]; ok { if _, err := CreateWebhookPost(newContext, post.ChannelId, text, respProps["username"], respProps["icon_url"], post.Props, post.Type); err != nil { @@ -706,7 +734,7 @@ func sendNotifications(c *Context, post *model.Post, team *model.Team, channel * TLSClientConfig: &tls.Config{InsecureSkipVerify: *utils.Cfg.ServiceSettings.EnableInsecureOutgoingConnections}, } httpClient := &http.Client{Transport: tr} - request, _ := http.NewRequest("POST", *utils.Cfg.EmailSettings.PushNotificationServer+"/api/v1/send_push", strings.NewReader(msg.ToJson())) + request, _ := http.NewRequest("POST", *utils.Cfg.EmailSettings.PushNotificationServer+model.API_URL_SUFFIX_V1+"/send_push", strings.NewReader(msg.ToJson())) l4g.Debug(utils.T("api.post.send_notifications_and_forget.push_notification.debug"), msg.DeviceId, msg.Message) if _, err := httpClient.Do(request); err != nil { @@ -719,7 +747,7 @@ func sendNotifications(c *Context, post *model.Post, team *model.Team, channel * } } - message := model.NewMessage(c.Session.TeamId, post.ChannelId, post.UserId, model.ACTION_POSTED) + message := model.NewMessage(c.TeamId, post.ChannelId, post.UserId, model.ACTION_POSTED) message.Add("post", post.ToJson()) message.Add("channel_type", channel.Type) @@ -780,7 +808,7 @@ func checkForOutOfChannelMentions(c *Context, post *model.Post, channel *model.C } SendEphemeralPost( - c.Session.TeamId, + c.TeamId, post.UserId, &model.Post{ ChannelId: post.ChannelId, @@ -847,7 +875,7 @@ func updatePost(c *Context, w http.ResponseWriter, r *http.Request) { return } - cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, post.ChannelId, c.Session.UserId) + cchan := Srv.Store.Channel().CheckPermissionsTo(c.TeamId, post.ChannelId, c.Session.UserId) pchan := Srv.Store.Post().Get(post.Id) if !c.HasPermissionsToChannel(cchan, "updatePost") { @@ -889,7 +917,7 @@ func updatePost(c *Context, w http.ResponseWriter, r *http.Request) { } else { rpost := result.Data.(*model.Post) - message := model.NewMessage(c.Session.TeamId, rpost.ChannelId, c.Session.UserId, model.ACTION_POST_EDITED) + message := model.NewMessage(c.TeamId, rpost.ChannelId, c.Session.UserId, model.ACTION_POST_EDITED) message.Add("post", rpost.ToJson()) PublishAndForget(message) @@ -901,7 +929,7 @@ func updatePost(c *Context, w http.ResponseWriter, r *http.Request) { func getPosts(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) - id := params["id"] + id := params["channel_id"] if len(id) != 26 { c.SetInvalidParam("getPosts", "channelId") return @@ -919,7 +947,7 @@ func getPosts(c *Context, w http.ResponseWriter, r *http.Request) { return } - cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, id, c.Session.UserId) + cchan := Srv.Store.Channel().CheckPermissionsTo(c.TeamId, id, c.Session.UserId) etagChan := Srv.Store.Post().GetEtag(id) if !c.HasPermissionsToChannel(cchan, "getPosts") { @@ -949,7 +977,7 @@ func getPosts(c *Context, w http.ResponseWriter, r *http.Request) { func getPostsSince(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) - id := params["id"] + id := params["channel_id"] if len(id) != 26 { c.SetInvalidParam("getPostsSince", "channelId") return @@ -961,7 +989,7 @@ func getPostsSince(c *Context, w http.ResponseWriter, r *http.Request) { return } - cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, id, c.Session.UserId) + cchan := Srv.Store.Channel().CheckPermissionsTo(c.TeamId, id, c.Session.UserId) pchan := Srv.Store.Post().GetPostsSince(id, time) if !c.HasPermissionsToChannel(cchan, "getPostsSince") { @@ -982,7 +1010,7 @@ func getPostsSince(c *Context, w http.ResponseWriter, r *http.Request) { func getPost(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) - channelId := params["id"] + channelId := params["channel_id"] if len(channelId) != 26 { c.SetInvalidParam("getPost", "channelId") return @@ -994,7 +1022,7 @@ func getPost(c *Context, w http.ResponseWriter, r *http.Request) { return } - cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, channelId, c.Session.UserId) + cchan := Srv.Store.Channel().CheckPermissionsTo(c.TeamId, channelId, c.Session.UserId) pchan := Srv.Store.Post().Get(postId) if !c.HasPermissionsToChannel(cchan, "getPost") { @@ -1041,7 +1069,7 @@ func getPostById(c *Context, w http.ResponseWriter, r *http.Request) { } post := list.Posts[list.Order[0]] - cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, post.ChannelId, c.Session.UserId) + cchan := Srv.Store.Channel().CheckPermissionsTo(c.TeamId, post.ChannelId, c.Session.UserId) if !c.HasPermissionsToChannel(cchan, "getPostById") { return } @@ -1058,7 +1086,7 @@ func getPostById(c *Context, w http.ResponseWriter, r *http.Request) { func deletePost(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) - channelId := params["id"] + channelId := params["channel_id"] if len(channelId) != 26 { c.SetInvalidParam("deletePost", "channelId") return @@ -1070,7 +1098,7 @@ func deletePost(c *Context, w http.ResponseWriter, r *http.Request) { return } - cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, channelId, c.Session.UserId) + cchan := Srv.Store.Channel().CheckPermissionsTo(c.TeamId, channelId, c.Session.UserId) pchan := Srv.Store.Post().Get(postId) if result := <-pchan; result.Err != nil { @@ -1106,11 +1134,11 @@ func deletePost(c *Context, w http.ResponseWriter, r *http.Request) { return } - message := model.NewMessage(c.Session.TeamId, post.ChannelId, c.Session.UserId, model.ACTION_POST_DELETED) + message := model.NewMessage(c.TeamId, post.ChannelId, c.Session.UserId, model.ACTION_POST_DELETED) message.Add("post", post.ToJson()) PublishAndForget(message) - DeletePostFilesAndForget(c.Session.TeamId, post) + DeletePostFilesAndForget(c.TeamId, post) result := make(map[string]string) result["id"] = postId @@ -1146,7 +1174,7 @@ func getPostsAfter(c *Context, w http.ResponseWriter, r *http.Request) { func getPostsBeforeOrAfter(c *Context, w http.ResponseWriter, r *http.Request, before bool) { params := mux.Vars(r) - id := params["id"] + id := params["channel_id"] if len(id) != 26 { c.SetInvalidParam("getPostsBeforeOrAfter", "channelId") return @@ -1170,7 +1198,7 @@ func getPostsBeforeOrAfter(c *Context, w http.ResponseWriter, r *http.Request, b return } - cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, id, c.Session.UserId) + cchan := Srv.Store.Channel().CheckPermissionsTo(c.TeamId, id, c.Session.UserId) // We can do better than this etag in this situation etagChan := Srv.Store.Post().GetEtag(id) @@ -1215,7 +1243,7 @@ func searchPosts(c *Context, w http.ResponseWriter, r *http.Request) { for _, params := range paramsList { // don't allow users to search for everything if params.Terms != "*" { - channels = append(channels, Srv.Store.Post().Search(c.Session.TeamId, c.Session.UserId, params)) + channels = append(channels, Srv.Store.Post().Search(c.TeamId, c.Session.UserId, params)) } } diff --git a/api/post_benchmark_test.go b/api/post_benchmark_test.go index 00eb3c468..4e5f6668f 100644 --- a/api/post_benchmark_test.go +++ b/api/post_benchmark_test.go @@ -16,7 +16,10 @@ func BenchmarkCreatePost(b *testing.B) { var ( NUM_POSTS_RANGE = utils.Range{NUM_POSTS, NUM_POSTS} ) - _, _, channel := SetupBenchmark() + + th := Setup().InitBasic() + Client := th.BasicClient + channel := th.BasicChannel testPoster := NewAutoPostCreator(Client, channel.Id) @@ -32,7 +35,10 @@ func BenchmarkUpdatePost(b *testing.B) { NUM_POSTS_RANGE = utils.Range{NUM_POSTS, NUM_POSTS} UPDATE_POST_LEN = 100 ) - _, _, channel := SetupBenchmark() + + th := Setup().InitBasic() + Client := th.BasicClient + channel := th.BasicChannel testPoster := NewAutoPostCreator(Client, channel.Id) posts, valid := testPoster.CreateTestPosts(NUM_POSTS_RANGE) @@ -59,7 +65,10 @@ func BenchmarkGetPosts(b *testing.B) { var ( NUM_POSTS_RANGE = utils.Range{NUM_POSTS, NUM_POSTS} ) - _, _, channel := SetupBenchmark() + + th := Setup().InitBasic() + Client := th.BasicClient + channel := th.BasicChannel testPoster := NewAutoPostCreator(Client, channel.Id) testPoster.CreateTestPosts(NUM_POSTS_RANGE) @@ -75,7 +84,10 @@ func BenchmarkSearchPosts(b *testing.B) { var ( NUM_POSTS_RANGE = utils.Range{NUM_POSTS, NUM_POSTS} ) - _, _, channel := SetupBenchmark() + + th := Setup().InitBasic() + Client := th.BasicClient + channel := th.BasicChannel testPoster := NewAutoPostCreator(Client, channel.Id) testPoster.CreateTestPosts(NUM_POSTS_RANGE) @@ -93,7 +105,10 @@ func BenchmarkEtagCache(b *testing.B) { var ( NUM_POSTS_RANGE = utils.Range{NUM_POSTS, NUM_POSTS} ) - _, _, channel := SetupBenchmark() + + th := Setup().InitBasic() + Client := th.BasicClient + channel := th.BasicChannel testPoster := NewAutoPostCreator(Client, channel.Id) testPoster.CreateTestPosts(NUM_POSTS_RANGE) @@ -111,7 +126,10 @@ func BenchmarkDeletePosts(b *testing.B) { var ( NUM_POSTS_RANGE = utils.Range{NUM_POSTS, NUM_POSTS} ) - _, _, channel := SetupBenchmark() + + th := Setup().InitBasic() + Client := th.BasicClient + channel := th.BasicChannel testPoster := NewAutoPostCreator(Client, channel.Id) posts, valid := testPoster.CreateTestPosts(NUM_POSTS_RANGE) diff --git a/api/post_test.go b/api/post_test.go index 2d978d3e3..b905c143e 100644 --- a/api/post_test.go +++ b/api/post_test.go @@ -5,38 +5,24 @@ package api import ( "github.com/mattermost/platform/model" - "github.com/mattermost/platform/store" "github.com/mattermost/platform/utils" "net/http" - "strings" + //"strings" + "fmt" "testing" "time" ) func TestCreatePost(t *testing.T) { - Setup() - - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - team2 := &model.Team{DisplayName: "Name Team 2", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team2 = Client.Must(Client.CreateTeam(team2)).Data.(*model.Team) - - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - - user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user2.Id)) - - Client.LoginByEmail(team.Name, user1.Email, "pwd") - - channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} - channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) - - channel2 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} - channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel) + th := Setup().InitBasic() + Client := th.BasicClient + team := th.BasicTeam + team2 := th.CreateTeam(th.BasicClient) + user1 := th.BasicUser + user3 := th.CreateUser(th.BasicClient) + LinkUserToTeam(user3, team2) + channel1 := th.BasicChannel + channel2 := th.CreateChannel(Client, team) filenames := []string{"/12345678901234567890123456/12345678901234567890123456/12345678901234567890123456/test.png", "/" + channel1.Id + "/" + user1.Id + "/test.png", "www.mattermost.com/fake/url", "junk"} @@ -97,21 +83,17 @@ func TestCreatePost(t *testing.T) { t.Fatal("Should have been forbidden") } - Client.LoginByEmail(team.Name, user2.Email, "pwd") + th.LoginBasic2() + post7 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"} _, err = Client.CreatePost(post7) if err.StatusCode != http.StatusForbidden { t.Fatal("Should have been forbidden") } - user3 := &model.User{TeamId: team2.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user3 = Client.Must(Client.CreateUser(user3, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user3.Id)) - - Client.LoginByEmail(team2.Name, user3.Email, "pwd") - - channel3 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team2.Id} - channel3 = Client.Must(Client.CreateChannel(channel3)).Data.(*model.Channel) + Client.LoginByEmail(team2.Name, user3.Email, user3.Password) + Client.SetTeamId(team2.Id) + channel3 := th.CreateChannel(Client, team2) post8 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"} _, err = Client.CreatePost(post8) @@ -125,29 +107,9 @@ func TestCreatePost(t *testing.T) { } func TestUpdatePost(t *testing.T) { - Setup() - - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - team2 := &model.Team{DisplayName: "Name Team 2", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team2 = Client.Must(Client.CreateTeam(team2)).Data.(*model.Team) - - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - - user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user2.Id)) - - Client.LoginByEmail(team.Name, user1.Email, "pwd") - - channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} - channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) - - channel2 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} - channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel) + th := Setup().InitBasic() + Client := th.BasicClient + channel1 := th.BasicChannel post1 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"} rpost1, err := Client.CreatePost(post1) @@ -196,19 +158,9 @@ func TestUpdatePost(t *testing.T) { } func TestGetPosts(t *testing.T) { - Setup() - - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - - Client.LoginByEmail(team.Name, user1.Email, "pwd") - - channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} - channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) + th := Setup().InitBasic() + Client := th.BasicClient + channel1 := th.BasicChannel time.Sleep(10 * time.Millisecond) post1 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"} @@ -261,19 +213,9 @@ func TestGetPosts(t *testing.T) { } func TestGetPostsSince(t *testing.T) { - Setup() - - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - - Client.LoginByEmail(team.Name, user1.Email, "pwd") - - channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} - channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) + th := Setup().InitBasic() + Client := th.BasicClient + channel1 := th.BasicChannel time.Sleep(10 * time.Millisecond) post0 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"} @@ -331,19 +273,9 @@ func TestGetPostsSince(t *testing.T) { } func TestGetPostsBeforeAfter(t *testing.T) { - Setup() - - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - - Client.LoginByEmail(team.Name, user1.Email, "pwd") - - channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} - channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) + th := Setup().InitBasic() + Client := th.BasicClient + channel1 := th.BasicChannel time.Sleep(10 * time.Millisecond) post0 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"} @@ -379,7 +311,8 @@ func TestGetPostsBeforeAfter(t *testing.T) { t.Fatal("wrong order") } - if len(r1.Posts) != 2 { + if len(r1.Posts) != 3 { + t.Log(r1.Posts) t.Fatal("wrong size") } @@ -408,19 +341,9 @@ func TestGetPostsBeforeAfter(t *testing.T) { } func TestSearchPosts(t *testing.T) { - Setup() - - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - - Client.LoginByEmail(team.Name, user1.Email, "pwd") - - channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} - channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) + th := Setup().InitBasic() + Client := th.BasicClient + channel1 := th.BasicChannel post1 := &model.Post{ChannelId: channel1.Id, Message: "search for post1"} post1 = Client.Must(Client.CreatePost(post1)).Data.(*model.Post) @@ -458,19 +381,9 @@ func TestSearchPosts(t *testing.T) { } func TestSearchHashtagPosts(t *testing.T) { - Setup() - - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - - Client.LoginByEmail(team.Name, user1.Email, "pwd") - - channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} - channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) + th := Setup().InitBasic() + Client := th.BasicClient + channel1 := th.BasicChannel post1 := &model.Post{ChannelId: channel1.Id, Message: "#sgtitlereview with space"} post1 = Client.Must(Client.CreatePost(post1)).Data.(*model.Post) @@ -489,19 +402,10 @@ func TestSearchHashtagPosts(t *testing.T) { } func TestSearchPostsInChannel(t *testing.T) { - Setup() - - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - - Client.LoginByEmail(team.Name, user1.Email, "pwd") - - channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} - channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) + th := Setup().InitBasic() + Client := th.BasicClient + channel1 := th.BasicChannel + team := th.BasicTeam post1 := &model.Post{ChannelId: channel1.Id, Message: "sgtitlereview with space"} post1 = Client.Must(Client.CreatePost(post1)).Data.(*model.Post) @@ -529,7 +433,7 @@ func TestSearchPostsInChannel(t *testing.T) { t.Fatalf("wrong number of posts returned %v", len(result.Order)) } - if result := Client.Must(Client.SearchPosts("channel:" + channel1.Name)).Data.(*model.PostList); len(result.Order) != 1 { + if result := Client.Must(Client.SearchPosts("channel:" + channel1.Name)).Data.(*model.PostList); len(result.Order) != 2 { t.Fatalf("wrong number of posts returned %v", len(result.Order)) } @@ -567,38 +471,29 @@ func TestSearchPostsInChannel(t *testing.T) { } func TestSearchPostsFromUser(t *testing.T) { - Setup() - - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - - Client.LoginByEmail(team.Name, user1.Email, "pwd") - - channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} - channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) - - channel2 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} - channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel) + th := Setup().InitBasic() + Client := th.BasicClient + channel1 := th.BasicChannel + team := th.BasicTeam + user1 := th.BasicUser + user2 := th.BasicUser2 + channel2 := th.CreateChannel(Client, team) + Client.Must(Client.AddChannelMember(channel1.Id, th.BasicUser2.Id)) + Client.Must(Client.AddChannelMember(channel2.Id, th.BasicUser2.Id)) + user3 := th.CreateUser(Client) + LinkUserToTeam(user3, team) + Client.Must(Client.AddChannelMember(channel1.Id, user3.Id)) + Client.Must(Client.AddChannelMember(channel2.Id, user3.Id)) post1 := &model.Post{ChannelId: channel1.Id, Message: "sgtitlereview with space"} post1 = Client.Must(Client.CreatePost(post1)).Data.(*model.Post) - user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user2.Id)) - - Client.LoginByEmail(team.Name, user2.Email, "pwd") - Client.Must(Client.JoinChannel(channel1.Id)) - Client.Must(Client.JoinChannel(channel2.Id)) + th.LoginBasic2() post2 := &model.Post{ChannelId: channel2.Id, Message: "sgtitlereview\n with return"} post2 = Client.Must(Client.CreatePost(post2)).Data.(*model.Post) - if result := Client.Must(Client.SearchPosts("from: " + user1.Username)).Data.(*model.PostList); len(result.Order) != 1 { + if result := Client.Must(Client.SearchPosts("from: " + user1.Username)).Data.(*model.PostList); len(result.Order) != 2 { t.Fatalf("wrong number of posts returned %v", len(result.Order)) } @@ -617,13 +512,7 @@ func TestSearchPostsFromUser(t *testing.T) { t.Fatalf("wrong number of posts returned %v", len(result.Order)) } - user3 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user3 = Client.Must(Client.CreateUser(user3, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user3.Id)) - - Client.LoginByEmail(team.Name, user3.Email, "pwd") - Client.Must(Client.JoinChannel(channel1.Id)) - Client.Must(Client.JoinChannel(channel2.Id)) + Client.LoginByEmail(team.Name, user3.Email, user3.Password) // wait for the join/leave messages to be created for user3 since they're done asynchronously time.Sleep(100 * time.Millisecond) @@ -649,19 +538,9 @@ func TestSearchPostsFromUser(t *testing.T) { } func TestGetPostsCache(t *testing.T) { - Setup() - - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - - Client.LoginByEmail(team.Name, user1.Email, "pwd") - - channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} - channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) + th := Setup().InitBasic() + Client := th.BasicClient + channel1 := th.BasicChannel time.Sleep(10 * time.Millisecond) post1 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"} @@ -698,23 +577,10 @@ func TestGetPostsCache(t *testing.T) { } func TestDeletePosts(t *testing.T) { - Setup() - - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - userAdmin := &model.User{TeamId: team.Id, Email: team.Email, Nickname: "Corey Hulen", Password: "pwd"} - userAdmin = Client.Must(Client.CreateUser(userAdmin, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(userAdmin.Id)) - - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - - Client.LoginByEmail(team.Name, user1.Email, "pwd") - - channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} - channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) + th := Setup().InitBasic() + Client := th.BasicClient + channel1 := th.BasicChannel + UpdateUserToTeamAdmin(th.BasicUser2, th.BasicTeam) time.Sleep(10 * time.Millisecond) post1 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"} @@ -745,7 +611,7 @@ func TestDeletePosts(t *testing.T) { r2 := Client.Must(Client.GetPosts(channel1.Id, 0, 10, "")).Data.(*model.PostList) - if len(r2.Posts) != 4 { + if len(r2.Posts) != 5 { t.Fatal("should have returned 4 items") } @@ -753,27 +619,17 @@ func TestDeletePosts(t *testing.T) { post4 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"} post4 = Client.Must(Client.CreatePost(post4)).Data.(*model.Post) - Client.LoginByEmail(team.Name, userAdmin.Email, "pwd") + th.LoginBasic2() Client.Must(Client.DeletePost(channel1.Id, post4.Id)) } func TestEmailMention(t *testing.T) { - Setup() - - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user1 := &model.User{TeamId: team.Id, Email: "success+test@simulator.amazonses.com", Nickname: "Bob Bobby", Password: "pwd"} - user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user1.Id)) + th := Setup().InitBasic() + Client := th.BasicClient + channel1 := th.BasicChannel - Client.LoginByEmail(team.Name, user1.Email, "pwd") - - channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} - channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) - - post1 := &model.Post{ChannelId: channel1.Id, Message: "bob"} + post1 := &model.Post{ChannelId: channel1.Id, Message: th.BasicUser.Username} post1 = Client.Must(Client.CreatePost(post1)).Data.(*model.Post) // No easy way to verify the email was sent, but this will at least cause the server to throw errors if the code is broken @@ -781,19 +637,9 @@ func TestEmailMention(t *testing.T) { } func TestFuzzyPosts(t *testing.T) { - Setup() - - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user.Id)) - - Client.LoginByEmail(team.Name, user.Email, "pwd") - - channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} - channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) + th := Setup().InitBasic() + Client := th.BasicClient + channel1 := th.BasicChannel filenames := []string{"junk"} @@ -808,21 +654,13 @@ func TestFuzzyPosts(t *testing.T) { } func TestMakeDirectChannelVisible(t *testing.T) { - Setup() + th := Setup().InitBasic() + Client := th.BasicClient + team := th.BasicTeam + user1 := th.BasicUser + user2 := th.BasicUser2 - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - - user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user2.Id)) - - // user2 will be created with prefs created to show user1 in the sidebar so set that to false to get rid of it - Client.LoginByEmail(team.Name, user2.Email, "pwd") + th.LoginBasic2() preferences := &model.Preferences{ { @@ -834,9 +672,11 @@ func TestMakeDirectChannelVisible(t *testing.T) { } Client.Must(Client.SetPreferences(preferences)) - Client.LoginByEmail(team.Name, user1.Email, "pwd") + Client.Must(Client.Logout()) + th.LoginBasic() + th.BasicClient.SetTeamId(team.Id) - channel := Client.Must(Client.CreateDirectChannel(map[string]string{"user_id": user2.Id})).Data.(*model.Channel) + channel := Client.Must(Client.CreateDirectChannel(user2.Id)).Data.(*model.Channel) makeDirectChannelVisible(team.Id, channel.Id) @@ -845,38 +685,17 @@ func TestMakeDirectChannelVisible(t *testing.T) { } else if pref := result.Data.(*model.Preference); pref.Value != "true" { t.Fatal("Failed to set direct channel to be visible for user1") } - - Client.LoginByEmail(team.Name, user2.Email, "pwd") - - if result, err := Client.GetPreference(model.PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW, user1.Id); err != nil { - t.Fatal("Errored trying to set direct channel to be visible for user2") - } else if pref := result.Data.(*model.Preference); pref.Value != "true" { - t.Fatal("Failed to set direct channel to be visible for user2") - } } func TestGetOutOfChannelMentions(t *testing.T) { - Setup() - - team1 := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Type: model.TEAM_OPEN} - team1 = Client.Must(Client.CreateTeam(team1)).Data.(*model.Team) - - user1 := &model.User{TeamId: team1.Id, Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd", Username: "user1"} - user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - - user2 := &model.User{TeamId: team1.Id, Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd", Username: "user2"} - user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user2.Id)) - - user3 := &model.User{TeamId: team1.Id, Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd", Username: "user3"} - user3 = Client.Must(Client.CreateUser(user3, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user3.Id)) - - Client.Must(Client.LoginByEmail(team1.Name, user1.Email, "pwd")) - - channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team1.Id} - channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) + th := Setup().InitBasic() + Client := th.BasicClient + channel1 := th.BasicChannel + team1 := th.BasicTeam + user1 := th.BasicUser + user2 := th.BasicUser2 + user3 := th.CreateUser(Client) + LinkUserToTeam(user3, team1) var allProfiles map[string]*model.User if result := <-Srv.Store.User().GetProfiles(team1.Id); result.Err != nil { @@ -893,39 +712,37 @@ func TestGetOutOfChannelMentions(t *testing.T) { } // test a post that doesn't @mention anybody - post1 := &model.Post{ChannelId: channel1.Id, Message: "user1 user2 user3"} + post1 := &model.Post{ChannelId: channel1.Id, Message: fmt.Sprintf("%v %v %v", user1.Username, user2.Username, user3.Username)} if mentioned := getOutOfChannelMentions(post1, allProfiles, members); len(mentioned) != 0 { t.Fatalf("getOutOfChannelMentions returned %v when no users were mentioned", mentioned) } // test a post that @mentions someone in the channel - post2 := &model.Post{ChannelId: channel1.Id, Message: "@user1 is user1"} + post2 := &model.Post{ChannelId: channel1.Id, Message: fmt.Sprintf("@%v is %v", user1.Username, user1.Username)} if mentioned := getOutOfChannelMentions(post2, allProfiles, members); len(mentioned) != 0 { t.Fatalf("getOutOfChannelMentions returned %v when only users in the channel were mentioned", mentioned) } // test a post that @mentions someone not in the channel - post3 := &model.Post{ChannelId: channel1.Id, Message: "@user2 and @user3 aren't in the channel"} + post3 := &model.Post{ChannelId: channel1.Id, Message: fmt.Sprintf("@%v and @%v aren't in the channel", user2.Username, user3.Username)} if mentioned := getOutOfChannelMentions(post3, allProfiles, members); len(mentioned) != 2 || (mentioned[0].Id != user2.Id && mentioned[0].Id != user3.Id) || (mentioned[1].Id != user2.Id && mentioned[1].Id != user3.Id) { t.Fatalf("getOutOfChannelMentions returned %v when two users outside the channel were mentioned", mentioned) } // test a post that @mentions someone not in the channel as well as someone in the channel - post4 := &model.Post{ChannelId: channel1.Id, Message: "@user2 and @user1 might be in the channel"} + post4 := &model.Post{ChannelId: channel1.Id, Message: fmt.Sprintf("@%v and @%v might be in the channel", user2.Username, user1.Username)} if mentioned := getOutOfChannelMentions(post4, allProfiles, members); len(mentioned) != 1 || mentioned[0].Id != user2.Id { t.Fatalf("getOutOfChannelMentions returned %v when someone in the channel and someone outside the channel were mentioned", mentioned) } Client.Must(Client.Logout()) - team2 := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Type: model.TEAM_OPEN} - team2 = Client.Must(Client.CreateTeam(team2)).Data.(*model.Team) - - user4 := &model.User{TeamId: team2.Id, Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd", Username: "user4"} - user4 = Client.Must(Client.CreateUser(user4, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user4.Id)) + team2 := th.CreateTeam(Client) + user4 := th.CreateUser(Client) + LinkUserToTeam(user4, team2) - Client.Must(Client.LoginByEmail(team2.Name, user4.Email, "pwd")) + Client.Must(Client.LoginByEmail(team2.Name, user4.Email, user4.Password)) + Client.SetTeamId(team2.Id) channel2 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team2.Id} channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel) @@ -943,7 +760,7 @@ func TestGetOutOfChannelMentions(t *testing.T) { } // test a post that @mentions someone on a different team - post5 := &model.Post{ChannelId: channel2.Id, Message: "@user2 and @user3 might be in the channel"} + post5 := &model.Post{ChannelId: channel2.Id, Message: fmt.Sprintf("@%v and @%v might be in the channel", user2.Username, user3.Username)} if mentioned := getOutOfChannelMentions(post5, allProfiles, members); len(mentioned) != 0 { t.Fatalf("getOutOfChannelMentions returned %v when two users on a different team were mentioned", mentioned) } diff --git a/api/preference.go b/api/preference.go index 9550b6c92..d9ddb1a21 100644 --- a/api/preference.go +++ b/api/preference.go @@ -11,14 +11,13 @@ import ( "net/http" ) -func InitPreference(r *mux.Router) { +func InitPreference() { l4g.Debug(utils.T("api.preference.init.debug")) - sr := r.PathPrefix("/preferences").Subrouter() - sr.Handle("/", ApiUserRequired(getAllPreferences)).Methods("GET") - sr.Handle("/save", ApiUserRequired(savePreferences)).Methods("POST") - sr.Handle("/{category:[A-Za-z0-9_]+}", ApiUserRequired(getPreferenceCategory)).Methods("GET") - sr.Handle("/{category:[A-Za-z0-9_]+}/{name:[A-Za-z0-9_]+}", ApiUserRequired(getPreference)).Methods("GET") + BaseRoutes.Preferences.Handle("/", ApiUserRequired(getAllPreferences)).Methods("GET") + BaseRoutes.Preferences.Handle("/save", ApiUserRequired(savePreferences)).Methods("POST") + BaseRoutes.Preferences.Handle("/{category:[A-Za-z0-9_]+}", ApiUserRequired(getPreferenceCategory)).Methods("GET") + BaseRoutes.Preferences.Handle("/{category:[A-Za-z0-9_]+}/{name:[A-Za-z0-9_]+}", ApiUserRequired(getPreference)).Methods("GET") } func getAllPreferences(c *Context, w http.ResponseWriter, r *http.Request) { @@ -44,7 +43,7 @@ func savePreferences(c *Context, w http.ResponseWriter, r *http.Request) { c.Err = model.NewLocAppError("savePreferences", "api.preference.save_preferences.set.app_error", nil, c.T("api.preference.save_preferences.set_details.app_error", map[string]interface{}{"SessionUserId": c.Session.UserId, "PreferenceUserId": preference.UserId})) - c.Err.StatusCode = http.StatusUnauthorized + c.Err.StatusCode = http.StatusForbidden return } } diff --git a/api/preference_test.go b/api/preference_test.go index 82bee6315..082f02527 100644 --- a/api/preference_test.go +++ b/api/preference_test.go @@ -5,23 +5,13 @@ package api import ( "github.com/mattermost/platform/model" - "github.com/mattermost/platform/store" "testing" ) func TestGetAllPreferences(t *testing.T) { - Setup() - - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - - user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user2.Id)) + th := Setup().InitBasic() + Client := th.BasicClient + user1 := th.BasicUser category := model.NewId() @@ -43,7 +33,6 @@ func TestGetAllPreferences(t *testing.T) { }, } - Client.LoginByEmail(team.Name, user1.Email, "pwd") Client.Must(Client.SetPreferences(&preferences1)) if result, err := Client.GetAllPreferences(); err != nil { @@ -52,27 +41,19 @@ func TestGetAllPreferences(t *testing.T) { t.Fatal("received the wrong number of preferences") } - Client.LoginByEmail(team.Name, user2.Email, "pwd") + th.LoginBasic2() - // note that user2 will automatically have a preference set for them to show user1 for direct messages if result, err := Client.GetAllPreferences(); err != nil { t.Fatal(err) - } else if data := result.Data.(model.Preferences); len(data) != 2 { + } else if data := result.Data.(model.Preferences); len(data) == 0 { t.Fatal("received the wrong number of preferences") } } func TestSetPreferences(t *testing.T) { - Setup() - - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - - Client.LoginByEmail(team.Name, user1.Email, "pwd") + th := Setup().InitBasic() + Client := th.BasicClient + user1 := th.BasicUser // save 10 preferences var preferences model.Preferences @@ -98,12 +79,7 @@ func TestSetPreferences(t *testing.T) { t.Fatal(err) } - // not able to update as a different user - user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user2.Id)) - - Client.LoginByEmail(team.Name, user2.Email, "pwd") + th.LoginBasic2() if _, err := Client.SetPreferences(&preferences); err == nil { t.Fatal("shouldn't have been able to update another user's preferences") @@ -111,18 +87,9 @@ func TestSetPreferences(t *testing.T) { } func TestGetPreferenceCategory(t *testing.T) { - Setup() - - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - - user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user2.Id)) + th := Setup().InitBasic() + Client := th.BasicClient + user1 := th.BasicUser category := model.NewId() @@ -144,11 +111,8 @@ func TestGetPreferenceCategory(t *testing.T) { }, } - Client.LoginByEmail(team.Name, user1.Email, "pwd") Client.Must(Client.SetPreferences(&preferences1)) - Client.LoginByEmail(team.Name, user1.Email, "pwd") - if result, err := Client.GetPreferenceCategory(category); err != nil { t.Fatal(err) } else if data := result.Data.(model.Preferences); len(data) != 2 { @@ -157,7 +121,7 @@ func TestGetPreferenceCategory(t *testing.T) { t.Fatal("received incorrect preferences") } - Client.LoginByEmail(team.Name, user2.Email, "pwd") + th.LoginBasic2() if result, err := Client.GetPreferenceCategory(category); err != nil { t.Fatal(err) @@ -167,16 +131,9 @@ func TestGetPreferenceCategory(t *testing.T) { } func TestGetPreference(t *testing.T) { - Setup() - - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user.Id)) - - Client.LoginByEmail(team.Name, user.Email, "pwd") + th := Setup().InitBasic() + Client := th.BasicClient + user := th.BasicUser preferences := model.Preferences{ { diff --git a/api/server.go b/api/server.go index b84066cbe..6e0ca49f0 100644 --- a/api/server.go +++ b/api/server.go @@ -6,6 +6,7 @@ package api import ( l4g "github.com/alecthomas/log4go" "github.com/braintree/manners" + "github.com/gorilla/handlers" "github.com/gorilla/mux" "github.com/mattermost/platform/store" "github.com/mattermost/platform/utils" @@ -73,11 +74,10 @@ func StartServer() { } go func() { - err := manners.ListenAndServe(utils.Cfg.ServiceSettings.ListenAddress, handler) + err := manners.ListenAndServe(utils.Cfg.ServiceSettings.ListenAddress, handlers.RecoveryHandler(handlers.PrintRecoveryStack(true))(handler)) if err != nil { l4g.Critical(utils.T("api.server.start_server.starting.critical"), err) time.Sleep(time.Second) - panic(utils.T("api.server.start_server.starting.panic") + err.Error()) } }() } diff --git a/api/sharding.go b/api/sharding.go deleted file mode 100644 index 2a5db408c..000000000 --- a/api/sharding.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -package api - -/* -func createSubDomain(subDomain string, target string) { - - if utils.Cfg.AWSSettings.Route53AccessKeyId == "" { - return - } - - creds := aws.Creds(utils.Cfg.AWSSettings.Route53AccessKeyId, utils.Cfg.AWSSettings.Route53SecretAccessKey, "") - r53 := route53.New(aws.DefaultConfig.Merge(&aws.Config{Credentials: creds, Region: utils.Cfg.AWSSettings.Route53Region})) - - rr := route53.ResourceRecord{ - Value: aws.String(target), - } - - rrs := make([]*route53.ResourceRecord, 1) - rrs[0] = &rr - - change := route53.Change{ - Action: aws.String("CREATE"), - ResourceRecordSet: &route53.ResourceRecordSet{ - Name: aws.String(fmt.Sprintf("%v.%v", subDomain, utils.Cfg.ServiceSettings.Domain)), - TTL: aws.Long(300), - Type: aws.String("CNAME"), - ResourceRecords: rrs, - }, - } - - changes := make([]*route53.Change, 1) - changes[0] = &change - - r53req := &route53.ChangeResourceRecordSetsInput{ - HostedZoneID: aws.String(utils.Cfg.AWSSettings.Route53ZoneId), - ChangeBatch: &route53.ChangeBatch{ - Changes: changes, - }, - } - - if _, err := r53.ChangeResourceRecordSets(r53req); err != nil { - l4g.Error("erro in createSubDomain domain=%v err=%v", subDomain, err) - return - } -} - -func doesSubDomainExist(subDomain string) bool { - - // if it's configured for testing then skip this step - if utils.Cfg.AWSSettings.Route53AccessKeyId == "" { - return false - } - - creds := aws.Creds(utils.Cfg.AWSSettings.Route53AccessKeyId, utils.Cfg.AWSSettings.Route53SecretAccessKey, "") - r53 := route53.New(aws.DefaultConfig.Merge(&aws.Config{Credentials: creds, Region: utils.Cfg.AWSSettings.Route53Region})) - - r53req := &route53.ListResourceRecordSetsInput{ - HostedZoneID: aws.String(utils.Cfg.AWSSettings.Route53ZoneId), - MaxItems: aws.String("1"), - StartRecordName: aws.String(fmt.Sprintf("%v.%v.", subDomain, utils.Cfg.ServiceSettings.Domain)), - } - - if result, err := r53.ListResourceRecordSets(r53req); err != nil { - l4g.Error("error in doesSubDomainExist domain=%v err=%v", subDomain, err) - return true - } else { - - for _, v := range result.ResourceRecordSets { - if v.Name != nil && *v.Name == fmt.Sprintf("%v.%v.", subDomain, utils.Cfg.ServiceSettings.Domain) { - return true - } - } - } - - return false -} -*/ diff --git a/api/slackimport.go b/api/slackimport.go index 5ca209c5c..4319fe409 100644 --- a/api/slackimport.go +++ b/api/slackimport.go @@ -112,7 +112,6 @@ func SlackAddUsers(teamId string, slackusers []SlackUser, log *bytes.Buffer) map password := model.NewId() newUser := model.User{ - TeamId: teamId, Username: sUser.Username, FirstName: firstName, LastName: lastName, @@ -120,7 +119,7 @@ func SlackAddUsers(teamId string, slackusers []SlackUser, log *bytes.Buffer) map Password: password, } - if mUser := ImportUser(&newUser); mUser != nil { + if mUser := ImportUser(teamId, &newUser); mUser != nil { addedUsers[sUser.Id] = mUser log.WriteString(utils.T("api.slackimport.slack_add_users.email_pwd", map[string]interface{}{"Email": newUser.Email, "Password": password})) } else { diff --git a/api/team.go b/api/team.go index 255982522..eefdc3d85 100644 --- a/api/team.go +++ b/api/team.go @@ -6,38 +6,43 @@ package api import ( "bytes" "fmt" - l4g "github.com/alecthomas/log4go" - "github.com/gorilla/mux" - "github.com/mattermost/platform/einterfaces" - "github.com/mattermost/platform/model" - "github.com/mattermost/platform/store" - "github.com/mattermost/platform/utils" "html/template" "net/http" "net/url" "strconv" "strings" "time" + + l4g "github.com/alecthomas/log4go" + "github.com/gorilla/mux" + + "github.com/mattermost/platform/model" + "github.com/mattermost/platform/utils" ) -func InitTeam(r *mux.Router) { +func InitTeam() { l4g.Debug(utils.T("api.team.init.debug")) - sr := r.PathPrefix("/teams").Subrouter() - sr.Handle("/create", ApiAppHandler(createTeam)).Methods("POST") - sr.Handle("/create_from_signup", ApiAppHandler(createTeamFromSignup)).Methods("POST") - sr.Handle("/create_with_ldap", ApiAppHandler(createTeamWithLdap)).Methods("POST") - sr.Handle("/create_with_sso/{service:[A-Za-z]+}", ApiAppHandler(createTeamFromSSO)).Methods("POST") - sr.Handle("/signup", ApiAppHandler(signupTeam)).Methods("POST") - sr.Handle("/all", ApiAppHandler(getAll)).Methods("GET") - sr.Handle("/find_team_by_name", ApiAppHandler(findTeamByName)).Methods("POST") - sr.Handle("/invite_members", ApiUserRequired(inviteMembers)).Methods("POST") - sr.Handle("/update", ApiUserRequired(updateTeam)).Methods("POST") - sr.Handle("/me", ApiUserRequired(getMyTeam)).Methods("GET") - sr.Handle("/get_invite_info", ApiAppHandler(getInviteInfo)).Methods("POST") + BaseRoutes.Teams.Handle("/create", ApiAppHandler(createTeam)).Methods("POST") + BaseRoutes.Teams.Handle("/create_from_signup", ApiAppHandler(createTeamFromSignup)).Methods("POST") + BaseRoutes.Teams.Handle("/signup", ApiAppHandler(signupTeam)).Methods("POST") + BaseRoutes.Teams.Handle("/all", ApiAppHandler(getAll)).Methods("GET") + BaseRoutes.Teams.Handle("/all_team_listings", ApiUserRequired(GetAllTeamListings)).Methods("GET") + BaseRoutes.Teams.Handle("/get_invite_info", ApiAppHandler(getInviteInfo)).Methods("POST") + BaseRoutes.Teams.Handle("/find_team_by_name", ApiAppHandler(findTeamByName)).Methods("POST") + BaseRoutes.Teams.Handle("/members/{id:[A-Za-z0-9]+}", ApiUserRequired(getMembers)).Methods("GET") + + BaseRoutes.NeedTeam.Handle("/me", ApiUserRequired(getMyTeam)).Methods("GET") + BaseRoutes.NeedTeam.Handle("/update", ApiUserRequired(updateTeam)).Methods("POST") + + BaseRoutes.NeedTeam.Handle("/invite_members", ApiUserRequired(inviteMembers)).Methods("POST") + + BaseRoutes.NeedTeam.Handle("/add_user_to_team", ApiUserRequired(addUserToTeam)).Methods("POST") + // These should be moved to the global admain console - sr.Handle("/import_team", ApiUserRequired(importTeam)).Methods("POST") - sr.Handle("/export_team", ApiUserRequired(exportTeam)).Methods("GET") + BaseRoutes.Teams.Handle("/import_team", ApiUserRequired(importTeam)).Methods("POST") + BaseRoutes.Teams.Handle("/export_team", ApiUserRequired(exportTeam)).Methods("GET") + BaseRoutes.Teams.Handle("/add_user_to_team_from_invite", ApiUserRequired(addUserToTeamFromInvite)).Methods("POST") } func signupTeam(c *Context, w http.ResponseWriter, r *http.Request) { @@ -92,67 +97,6 @@ func signupTeam(c *Context, w http.ResponseWriter, r *http.Request) { w.Write([]byte(model.MapToJson(m))) } -func createTeamFromSSO(c *Context, w http.ResponseWriter, r *http.Request) { - params := mux.Vars(r) - service := params["service"] - - sso := utils.Cfg.GetSSOService(service) - if sso != nil && !sso.Enable { - c.SetInvalidParam("createTeamFromSSO", "service") - return - } - - team := model.TeamFromJson(r.Body) - - if team == nil { - c.SetInvalidParam("createTeamFromSSO", "team") - return - } - - if !isTeamCreationAllowed(c, team.Email) { - return - } - - team.PreSave() - - team.Name = model.CleanTeamName(team.Name) - - if err := team.IsValid(*utils.Cfg.TeamSettings.RestrictTeamNames); err != nil { - c.Err = err - return - } - - team.Id = "" - - found := true - count := 0 - for found { - if found = FindTeamByName(c, team.Name, "true"); c.Err != nil { - return - } else if found { - team.Name = team.Name + strconv.Itoa(count) - count += 1 - } - } - - if result := <-Srv.Store.Team().Save(team); result.Err != nil { - c.Err = result.Err - return - } else { - rteam := result.Data.(*model.Team) - - if _, err := CreateDefaultChannels(c, rteam.Id); err != nil { - c.Err = nil - return - } - - data := map[string]string{"follow_link": c.GetSiteURL() + "/api/v1/oauth/" + service + "/signup?team=" + rteam.Name} - w.Write([]byte(model.MapToJson(data))) - - } - -} - func createTeamFromSignup(c *Context, w http.ResponseWriter, r *http.Request) { if !utils.Cfg.EmailSettings.EnableSignUpWithEmail { c.Err = model.NewLocAppError("createTeamFromSignup", "api.team.create_team_from_signup.email_disabled.app_error", nil, "") @@ -186,13 +130,11 @@ func createTeamFromSignup(c *Context, w http.ResponseWriter, r *http.Request) { password := teamSignup.User.Password teamSignup.User.PreSave() - teamSignup.User.TeamId = model.NewId() if err := teamSignup.User.IsValid(); err != nil { c.Err = err return } teamSignup.User.Id = "" - teamSignup.User.TeamId = "" teamSignup.User.Password = password if !model.ComparePassword(teamSignup.Hash, fmt.Sprintf("%v:%v", teamSignup.Data, utils.Cfg.EmailSettings.InviteSalt)) { @@ -206,10 +148,7 @@ func createTeamFromSignup(c *Context, w http.ResponseWriter, r *http.Request) { return } - found := FindTeamByName(c, teamSignup.Team.Name, "true") - if c.Err != nil { - return - } + found := FindTeamByName(teamSignup.Team.Name) if found { c.Err = model.NewLocAppError("createTeamFromSignup", "api.team.create_team_from_signup.unavailable.app_error", nil, "d="+teamSignup.Team.Name) @@ -227,15 +166,16 @@ func createTeamFromSignup(c *Context, w http.ResponseWriter, r *http.Request) { return } - teamSignup.User.TeamId = rteam.Id teamSignup.User.EmailVerified = true - ruser, err := CreateUser(rteam, &teamSignup.User) + ruser, err := CreateUser(&teamSignup.User) if err != nil { c.Err = err return } + JoinUserToTeam(rteam, ruser) + InviteMembers(c, rteam, ruser, teamSignup.Invites) teamSignup.Team = *rteam @@ -245,85 +185,38 @@ func createTeamFromSignup(c *Context, w http.ResponseWriter, r *http.Request) { } } -func createTeamWithLdap(c *Context, w http.ResponseWriter, r *http.Request) { - ldap := einterfaces.GetLdapInterface() - if ldap == nil { - c.Err = model.NewLocAppError("createTeamWithLdap", "ent.ldap.do_login.licence_disable.app_error", nil, "") - return - } - - teamSignup := model.TeamSignupFromJson(r.Body) +func createTeam(c *Context, w http.ResponseWriter, r *http.Request) { + team := model.TeamFromJson(r.Body) - if teamSignup == nil { - c.SetInvalidParam("createTeam", "teamSignup") + if team == nil { + c.SetInvalidParam("createTeam", "team") return } - teamSignup.Team.PreSave() - - if err := teamSignup.Team.IsValid(*utils.Cfg.TeamSettings.RestrictTeamNames); err != nil { - c.Err = err - return - } + var user *model.User + if len(c.Session.UserId) > 0 { + uchan := Srv.Store.User().Get(c.Session.UserId) - if !isTeamCreationAllowed(c, teamSignup.Team.Email) { - return + if result := <-uchan; result.Err != nil { + c.Err = result.Err + return + } else { + user = result.Data.(*model.User) + team.Email = user.Email + } } - teamSignup.Team.Id = "" - - found := FindTeamByName(c, teamSignup.Team.Name, "true") + rteam := CreateTeam(c, team) if c.Err != nil { return } - if found { - c.Err = model.NewLocAppError("createTeamFromSignup", "api.team.create_team_from_signup.unavailable.app_error", nil, "d="+teamSignup.Team.Name) - return - } - - user, err := ldap.GetUser(teamSignup.User.Username) - if err != nil { - c.Err = err - return - } - - err = ldap.CheckPassword(teamSignup.User.Username, teamSignup.User.Password) - if err != nil { - c.Err = err - return - } - - if result := <-Srv.Store.Team().Save(&teamSignup.Team); result.Err != nil { - c.Err = result.Err - return - } else { - rteam := result.Data.(*model.Team) - - if _, err := CreateDefaultChannels(c, rteam.Id); err != nil { - c.Err = nil - return - } - - user.TeamId = rteam.Id - ruser, err := CreateUser(rteam, user) + if user != nil { + err := JoinUserToTeam(team, user) if err != nil { c.Err = err return } - - teamSignup.Team = *rteam - teamSignup.User = *ruser - - w.Write([]byte(teamSignup.ToJson())) - } -} - -func createTeam(c *Context, w http.ResponseWriter, r *http.Request) { - team := model.TeamFromJson(r.Body) - rteam := CreateTeam(c, team) - if c.Err != nil { - return } w.Write([]byte(rteam.ToJson())) @@ -360,6 +253,31 @@ func CreateTeam(c *Context, team *model.Team) *model.Team { } } +func JoinUserToTeam(team *model.Team, user *model.User) *model.AppError { + + tm := &model.TeamMember{TeamId: team.Id, UserId: user.Id} + + channelRole := "" + if team.Email == user.Email { + tm.Roles = model.ROLE_TEAM_ADMIN + channelRole = model.CHANNEL_ROLE_ADMIN + } + + if tmr := <-Srv.Store.Team().SaveMember(tm); tmr.Err != nil { + return tmr.Err + } + + // Soft error if there is an issue joining the default channels + if err := JoinDefaultChannels(team.Id, user, channelRole); err != nil { + l4g.Error(utils.T("api.user.create_user.joining.error"), user.Id, team.Id, err) + } + + RemoveAllSessionsForUserId(user.Id) + InvalidateCacheForUser(user.Id) + + return nil +} + func isTeamCreationAllowed(c *Context, email string) bool { email = strings.ToLower(email) @@ -389,6 +307,24 @@ func isTeamCreationAllowed(c *Context, email string) bool { return true } +func GetAllTeamListings(c *Context, w http.ResponseWriter, r *http.Request) { + if result := <-Srv.Store.Team().GetAllTeamListing(); result.Err != nil { + c.Err = result.Err + return + } else { + teams := result.Data.([]*model.Team) + m := make(map[string]*model.Team) + for _, v := range teams { + m[v.Id] = v + if !c.IsSystemAdmin() { + m[v.Id].Sanitize() + } + } + + w.Write([]byte(model.TeamMapToJson(m))) + } +} + func getAll(c *Context, w http.ResponseWriter, r *http.Request) { if result := <-Srv.Store.Team().GetAll(); result.Err != nil { c.Err = result.Err @@ -435,52 +371,54 @@ func revokeAllSessions(c *Context, w http.ResponseWriter, r *http.Request) { } } -func findTeamByName(c *Context, w http.ResponseWriter, r *http.Request) { - - m := model.MapFromJson(r.Body) - - name := strings.ToLower(strings.TrimSpace(m["name"])) - all := strings.ToLower(strings.TrimSpace(m["all"])) +func inviteMembers(c *Context, w http.ResponseWriter, r *http.Request) { + invites := model.InvitesFromJson(r.Body) + if len(invites.Invites) == 0 { + c.Err = model.NewLocAppError("Team.InviteMembers", "api.team.invite_members.no_one.app_error", nil, "") + c.Err.StatusCode = http.StatusBadRequest + return + } - found := FindTeamByName(c, name, all) + tchan := Srv.Store.Team().Get(c.TeamId) + uchan := Srv.Store.User().Get(c.Session.UserId) - if c.Err != nil { + var team *model.Team + if result := <-tchan; result.Err != nil { + c.Err = result.Err return + } else { + team = result.Data.(*model.Team) } - if found { - w.Write([]byte("true")) + var user *model.User + if result := <-uchan; result.Err != nil { + c.Err = result.Err + return } else { - w.Write([]byte("false")) + user = result.Data.(*model.User) } -} - -func FindTeamByName(c *Context, name string, all string) bool { - if name == "" || len(name) > 64 { - c.SetInvalidParam("findTeamByName", "domain") - return false + ia := make([]string, len(invites.Invites)) + for _, invite := range invites.Invites { + ia = append(ia, invite["email"]) } - if result := <-Srv.Store.Team().GetByName(name); result.Err != nil { - return false - } else { - return true - } + InviteMembers(c, team, user, ia) - return false + w.Write([]byte(invites.ToJson())) } -func inviteMembers(c *Context, w http.ResponseWriter, r *http.Request) { - invites := model.InvitesFromJson(r.Body) - if len(invites.Invites) == 0 { - c.Err = model.NewLocAppError("Team.InviteMembers", "api.team.invite_members.no_one.app_error", nil, "") - c.Err.StatusCode = http.StatusBadRequest +func addUserToTeam(c *Context, w http.ResponseWriter, r *http.Request) { + params := model.MapFromJson(r.Body) + userId := params["user_id"] + + if len(userId) != 26 { + c.SetInvalidParam("addUserToTeam", "user_id") return } - tchan := Srv.Store.Team().Get(c.Session.TeamId) - uchan := Srv.Store.User().Get(c.Session.UserId) + tchan := Srv.Store.Team().Get(c.TeamId) + uchan := Srv.Store.User().Get(userId) var team *model.Team if result := <-tchan; result.Err != nil { @@ -498,23 +436,116 @@ func inviteMembers(c *Context, w http.ResponseWriter, r *http.Request) { user = result.Data.(*model.User) } - var invNum int64 = 0 - for i, invite := range invites.Invites { - if result := <-Srv.Store.User().GetByEmail(c.Session.TeamId, invite["email"]); result.Err == nil || result.Err.Id != store.MISSING_ACCOUNT_ERROR { - invNum = int64(i) - c.Err = model.NewLocAppError("invite_members", "api.team.invite_members.already.app_error", nil, strconv.FormatInt(invNum, 10)) + if !c.IsTeamAdmin() { + c.Err = model.NewLocAppError("addUserToTeam", "api.team.update_team.permissions.app_error", nil, "userId="+c.Session.UserId) + c.Err.StatusCode = http.StatusForbidden + return + } + + err := JoinUserToTeam(team, user) + if err != nil { + c.Err = err + return + } + + w.Write([]byte(model.MapToJson(params))) +} + +func addUserToTeamFromInvite(c *Context, w http.ResponseWriter, r *http.Request) { + + params := model.MapFromJson(r.Body) + hash := params["hash"] + data := params["data"] + inviteId := params["invite_id"] + + teamId := "" + var team *model.Team + + if len(hash) > 0 { + props := model.MapFromJson(strings.NewReader(data)) + + if !model.ComparePassword(hash, fmt.Sprintf("%v:%v", data, utils.Cfg.EmailSettings.InviteSalt)) { + c.Err = model.NewLocAppError("addUserToTeamFromInvite", "api.user.create_user.signup_link_invalid.app_error", nil, "") + return + } + + t, err := strconv.ParseInt(props["time"], 10, 64) + if err != nil || model.GetMillis()-t > 1000*60*60*48 { // 48 hours + c.Err = model.NewLocAppError("addUserToTeamFromInvite", "api.user.create_user.signup_link_expired.app_error", nil, "") + return + } + + teamId = props["id"] + + // try to load the team to make sure it exists + if result := <-Srv.Store.Team().Get(teamId); result.Err != nil { + c.Err = result.Err + return + } else { + team = result.Data.(*model.Team) + } + } + + if len(inviteId) > 0 { + if result := <-Srv.Store.Team().GetByInviteId(inviteId); result.Err != nil { + c.Err = result.Err + return + } else { + team = result.Data.(*model.Team) + teamId = team.Id + } + } + + if len(teamId) == 0 { + c.Err = model.NewLocAppError("addUserToTeamFromInvite", "api.user.create_user.signup_link_invalid.app_error", nil, "") + return + } + + uchan := Srv.Store.User().Get(c.Session.UserId) + + var user *model.User + if result := <-uchan; result.Err != nil { + c.Err = result.Err + return + } else { + user = result.Data.(*model.User) + } + + tm := c.Session.GetTeamByTeamId(teamId) + + if tm == nil { + err := JoinUserToTeam(team, user) + if err != nil { + c.Err = err return } } - ia := make([]string, len(invites.Invites)) - for _, invite := range invites.Invites { - ia = append(ia, invite["email"]) + team.Sanitize() + + w.Write([]byte(team.ToJson())) +} + +func FindTeamByName(name string) bool { + if result := <-Srv.Store.Team().GetByName(name); result.Err != nil { + return false + } else { + return true } +} - InviteMembers(c, team, user, ia) +func findTeamByName(c *Context, w http.ResponseWriter, r *http.Request) { - w.Write([]byte(invites.ToJson())) + m := model.MapFromJson(r.Body) + name := strings.ToLower(strings.TrimSpace(m["name"])) + + found := FindTeamByName(name) + + if found { + w.Write([]byte("true")) + } else { + w.Write([]byte("false")) + } } func InviteMembers(c *Context, team *model.Team, user *model.User, invites []string) { @@ -573,7 +604,7 @@ func updateTeam(c *Context, w http.ResponseWriter, r *http.Request) { return } - team.Id = c.Session.TeamId + team.Id = c.TeamId if !c.IsTeamAdmin() { c.Err = model.NewLocAppError("updateTeam", "api.team.update_team.permissions.app_error", nil, "userId="+c.Session.UserId) @@ -592,7 +623,6 @@ func updateTeam(c *Context, w http.ResponseWriter, r *http.Request) { oldTeam.DisplayName = team.DisplayName oldTeam.InviteId = team.InviteId oldTeam.AllowOpenInvite = team.AllowOpenInvite - oldTeam.AllowTeamListing = team.AllowTeamListing oldTeam.CompanyName = team.CompanyName oldTeam.AllowedDomains = team.AllowedDomains //oldTeam.Type = team.Type @@ -617,16 +647,11 @@ func PermanentDeleteTeam(c *Context, team *model.Team) *model.AppError { return result.Err } - if result := <-Srv.Store.User().GetForExport(team.Id); result.Err != nil { + if result := <-Srv.Store.Channel().PermanentDeleteByTeam(team.Id); result.Err != nil { return result.Err - } else { - users := result.Data.([]*model.User) - for _, user := range users { - PermanentDeleteUser(c, user) - } } - if result := <-Srv.Store.Channel().PermanentDeleteByTeam(team.Id); result.Err != nil { + if result := <-Srv.Store.Team().RemoveAllMembersByTeam(team.Id); result.Err != nil { return result.Err } @@ -642,11 +667,11 @@ func PermanentDeleteTeam(c *Context, team *model.Team) *model.AppError { func getMyTeam(c *Context, w http.ResponseWriter, r *http.Request) { - if len(c.Session.TeamId) == 0 { + if len(c.TeamId) == 0 { return } - if result := <-Srv.Store.Team().Get(c.Session.TeamId); result.Err != nil { + if result := <-Srv.Store.Team().Get(c.TeamId); result.Err != nil { c.Err = result.Err return } else if HandleEtag(result.Data.(*model.Team).Etag(), w, r) { @@ -659,7 +684,7 @@ func getMyTeam(c *Context, w http.ResponseWriter, r *http.Request) { } func importTeam(c *Context, w http.ResponseWriter, r *http.Request) { - if !c.HasPermissionsToTeam(c.Session.TeamId, "import") || !c.IsTeamAdmin() { + if !c.HasPermissionsToTeam(c.TeamId, "import") || !c.IsTeamAdmin() { c.Err = model.NewLocAppError("importTeam", "api.team.import_team.admin.app_error", nil, "userId="+c.Session.UserId) c.Err.StatusCode = http.StatusForbidden return @@ -714,7 +739,7 @@ func importTeam(c *Context, w http.ResponseWriter, r *http.Request) { switch importFrom { case "slack": var err *model.AppError - if err, log = SlackImport(fileData, fileSize, c.Session.TeamId); err != nil { + if err, log = SlackImport(fileData, fileSize, c.TeamId); err != nil { c.Err = err c.Err.StatusCode = http.StatusBadRequest } @@ -726,7 +751,7 @@ func importTeam(c *Context, w http.ResponseWriter, r *http.Request) { } func exportTeam(c *Context, w http.ResponseWriter, r *http.Request) { - if !c.HasPermissionsToTeam(c.Session.TeamId, "export") || !c.IsTeamAdmin() { + if !c.HasPermissionsToTeam(c.TeamId, "export") || !c.IsTeamAdmin() { c.Err = model.NewLocAppError("exportTeam", "api.team.export_team.admin.app_error", nil, "userId="+c.Session.UserId) c.Err.StatusCode = http.StatusForbidden return @@ -765,3 +790,23 @@ func getInviteInfo(c *Context, w http.ResponseWriter, r *http.Request) { w.Write([]byte(model.MapToJson(result))) } } + +func getMembers(c *Context, w http.ResponseWriter, r *http.Request) { + params := mux.Vars(r) + id := params["id"] + + if c.Session.GetTeamByTeamId(id) == nil { + if !c.HasSystemAdminPermissions("getMembers") { + return + } + } + + if result := <-Srv.Store.Team().GetMembers(id); result.Err != nil { + c.Err = result.Err + return + } else { + members := result.Data.([]*model.TeamMember) + w.Write([]byte(model.TeamMembersToJson(members))) + return + } +} diff --git a/api/team_test.go b/api/team_test.go index bbbc8385d..161c7e620 100644 --- a/api/team_test.go +++ b/api/team_test.go @@ -13,7 +13,9 @@ import ( ) func TestSignupTeam(t *testing.T) { - Setup() + th := Setup().InitBasic() + th.BasicClient.Logout() + Client := th.BasicClient _, err := Client.SignupTeam("test@nowhere.com", "name") if err != nil { @@ -22,7 +24,9 @@ func TestSignupTeam(t *testing.T) { } func TestCreateFromSignupTeam(t *testing.T) { - Setup() + th := Setup().InitBasic() + th.BasicClient.Logout() + Client := th.BasicClient props := make(map[string]string) props["email"] = strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com" @@ -47,6 +51,8 @@ func TestCreateFromSignupTeam(t *testing.T) { } ruser := rts.Data.(*model.TeamSignup).User + rteam := rts.Data.(*model.TeamSignup).Team + Client.SetTeamId(rteam.Id) if result, err := Client.LoginById(ruser.Id, user.Password); err != nil { t.Fatal(err) @@ -69,7 +75,9 @@ func TestCreateFromSignupTeam(t *testing.T) { } func TestCreateTeam(t *testing.T) { - Setup() + th := Setup().InitBasic() + th.BasicClient.Logout() + Client := th.BasicClient team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} rteam, err := Client.CreateTeam(&team) @@ -77,11 +85,13 @@ func TestCreateTeam(t *testing.T) { t.Fatal(err) } - user := &model.User{TeamId: rteam.Data.(*model.Team).Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} + user := &model.User{Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) + LinkUserToTeam(user, rteam.Data.(*model.Team)) store.Must(Srv.Store.User().VerifyEmail(user.Id)) Client.LoginByEmail(team.Name, user.Email, "pwd") + Client.SetTeamId(rteam.Data.(*model.Team).Id) c1 := Client.Must(Client.GetChannels("")).Data.(*model.ChannelList) if len(c1.Channels) != 2 { @@ -108,26 +118,176 @@ func TestCreateTeam(t *testing.T) { } } +func TestAddUserToTeam(t *testing.T) { + th := Setup().InitBasic() + th.BasicClient.Logout() + Client := th.BasicClient + + props := make(map[string]string) + props["email"] = strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com" + props["name"] = "Test Company name" + props["time"] = fmt.Sprintf("%v", model.GetMillis()) + + data := model.MapToJson(props) + hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.EmailSettings.InviteSalt)) + + team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: props["email"], Type: model.TEAM_OPEN} + user := model.User{Email: props["email"], Nickname: "Corey Hulen", Password: "hello"} + + ts := model.TeamSignup{Team: team, User: user, Invites: []string{"success+test@simulator.amazonses.com"}, Data: data, Hash: hash} + + rts, err := Client.CreateTeamFromSignup(&ts) + if err != nil { + t.Fatal(err) + } + + if rts.Data.(*model.TeamSignup).Team.DisplayName != team.DisplayName { + t.Fatal("full name didn't match") + } + + ruser := rts.Data.(*model.TeamSignup).User + rteam := rts.Data.(*model.TeamSignup).Team + Client.SetTeamId(rteam.Id) + + if result, err := Client.LoginById(ruser.Id, user.Password); err != nil { + t.Fatal(err) + } else { + if result.Data.(*model.User).Email != user.Email { + t.Fatal("email's didn't match") + } + } + + user2 := th.CreateUser(th.BasicClient) + if result, err := th.BasicClient.AddUserToTeam(user2.Id); err != nil { + t.Fatal(err) + } else { + rm := result.Data.(map[string]string) + if rm["user_id"] != user2.Id { + t.Fatal("email's didn't match") + } + } +} + +func TestAddUserToTeamFromInvite(t *testing.T) { + th := Setup().InitBasic() + th.BasicClient.Logout() + Client := th.BasicClient + + props := make(map[string]string) + props["email"] = strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com" + props["name"] = "Test Company name" + props["time"] = fmt.Sprintf("%v", model.GetMillis()) + + data := model.MapToJson(props) + hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.EmailSettings.InviteSalt)) + + team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: props["email"], Type: model.TEAM_OPEN} + user := model.User{Email: props["email"], Nickname: "Corey Hulen", Password: "hello"} + + ts := model.TeamSignup{Team: team, User: user, Invites: []string{"success+test@simulator.amazonses.com"}, Data: data, Hash: hash} + + rts, err := Client.CreateTeamFromSignup(&ts) + if err != nil { + t.Fatal(err) + } + + if rts.Data.(*model.TeamSignup).Team.DisplayName != team.DisplayName { + t.Fatal("full name didn't match") + } + + ruser := rts.Data.(*model.TeamSignup).User + rteam := rts.Data.(*model.TeamSignup).Team + Client.SetTeamId(rteam.Id) + + if result, err := Client.LoginById(ruser.Id, user.Password); err != nil { + t.Fatal(err) + } else { + if result.Data.(*model.User).Email != user.Email { + t.Fatal("email's didn't match") + } + } + + user2 := th.CreateUser(th.BasicClient) + Client.Must(Client.Logout()) + Client.Must(Client.LoginByEmail("", user2.Email, user2.Password)) + + if result, err := th.BasicClient.AddUserToTeamFromInvite("", "", rteam.InviteId); err != nil { + t.Fatal(err) + } else { + rtm := result.Data.(*model.Team) + if rtm.Id != rteam.Id { + t.Fatal() + } + } +} + func TestGetAllTeams(t *testing.T) { - Setup() + th := Setup().InitBasic() + th.BasicClient.Logout() + Client := th.BasicClient - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN, AllowTeamListing: true} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} + user := &model.User{Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) + LinkUserToTeam(user, team) store.Must(Srv.Store.User().VerifyEmail(user.Id)) Client.LoginByEmail(team.Name, user.Email, "pwd") + Client.SetTeamId(team.Id) + + if r1, err := Client.GetAllTeams(); err != nil { + t.Fatal(err) + } else { + teams := r1.Data.(map[string]*model.Team) + if teams[team.Id].Name != team.Name { + t.Fatal() + } + if teams[team.Id].Email != "" { + t.Fatal("Non admin users shoudn't get full listings") + } + } + + c := &Context{} + c.RequestId = model.NewId() + c.IpAddress = "cmd_line" + UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN) - enableIncomingHooks := *utils.Cfg.TeamSettings.EnableTeamListing - defer func() { - *utils.Cfg.TeamSettings.EnableTeamListing = enableIncomingHooks - }() - *utils.Cfg.TeamSettings.EnableTeamListing = true + Client.LoginByEmail(team.Name, user.Email, "pwd") + Client.SetTeamId(team.Id) if r1, err := Client.GetAllTeams(); err != nil { t.Fatal(err) + } else { + teams := r1.Data.(map[string]*model.Team) + if teams[team.Id].Name != team.Name { + t.Fatal() + } + if teams[team.Id].Email != team.Email { + t.Fatal() + } + } +} + +func TestGetAllTeamListings(t *testing.T) { + th := Setup().InitBasic() + th.BasicClient.Logout() + Client := th.BasicClient + + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN, AllowOpenInvite: true} + team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) + + user := &model.User{Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} + user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) + LinkUserToTeam(user, team) + store.Must(Srv.Store.User().VerifyEmail(user.Id)) + + Client.LoginByEmail(team.Name, user.Email, "pwd") + Client.SetTeamId(team.Id) + + if r1, err := Client.GetAllTeamListings(); err != nil { + t.Fatal(err) } else { teams := r1.Data.(map[string]*model.Team) if teams[team.Id].Name != team.Name { @@ -144,6 +304,7 @@ func TestGetAllTeams(t *testing.T) { UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN) Client.LoginByEmail(team.Name, user.Email, "pwd") + Client.SetTeamId(team.Id) if r1, err := Client.GetAllTeams(); err != nil { t.Fatal(err) @@ -159,16 +320,20 @@ func TestGetAllTeams(t *testing.T) { } func TestTeamPermDelete(t *testing.T) { - Setup() + th := Setup().InitBasic() + th.BasicClient.Logout() + Client := th.BasicClient team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} + user1 := &model.User{Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) + LinkUserToTeam(user1, team) store.Must(Srv.Store.User().VerifyEmail(user1.Id)) Client.LoginByEmail(team.Name, user1.Email, "pwd") + Client.SetTeamId(team.Id) channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) @@ -198,19 +363,23 @@ func TestTeamPermDelete(t *testing.T) { } func TestInviteMembers(t *testing.T) { - Setup() + th := Setup().InitBasic() + th.BasicClient.Logout() + Client := th.BasicClient team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} + user := &model.User{Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) + LinkUserToTeam(user, team) store.Must(Srv.Store.User().VerifyEmail(user.Id)) Client.LoginByEmail(team.Name, user.Email, "pwd") + Client.SetTeamId(team.Id) invite := make(map[string]string) - invite["email"] = model.NewId() + "success+test@simulator.amazonses.com" + invite["email"] = "success+" + model.NewId() + "@simulator.amazonses.com" invite["first_name"] = "Test" invite["last_name"] = "Guy" invites := &model.Invites{Invites: []map[string]string{invite}} @@ -227,20 +396,25 @@ func TestInviteMembers(t *testing.T) { } func TestUpdateTeamDisplayName(t *testing.T) { - Setup() + th := Setup().InitBasic() + th.BasicClient.Logout() + Client := th.BasicClient - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "success+" + model.NewId() + "@simulator.amazonses.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user := &model.User{TeamId: team.Id, Email: "test@nowhere.com", Nickname: "Corey Hulen", Password: "pwd"} + user := &model.User{Email: team.Email, Nickname: "Corey Hulen", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) + LinkUserToTeam(user, team) store.Must(Srv.Store.User().VerifyEmail(user.Id)) - user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} + user2 := &model.User{Email: "success+" + model.NewId() + "@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) + LinkUserToTeam(user2, team) store.Must(Srv.Store.User().VerifyEmail(user2.Id)) Client.LoginByEmail(team.Name, user2.Email, "pwd") + Client.SetTeamId(team.Id) vteam := &model.Team{DisplayName: team.DisplayName, Name: team.Name, Email: team.Email, Type: team.Type} vteam.DisplayName = "NewName" @@ -262,6 +436,9 @@ func TestUpdateTeamDisplayName(t *testing.T) { } func TestFuzzyTeamCreate(t *testing.T) { + th := Setup().InitBasic() + th.BasicClient.Logout() + Client := th.BasicClient for i := 0; i < len(utils.FUZZY_STRINGS_NAMES) || i < len(utils.FUZZY_STRINGS_EMAILS); i++ { testDisplayName := "Name" @@ -284,19 +461,24 @@ func TestFuzzyTeamCreate(t *testing.T) { } func TestGetMyTeam(t *testing.T) { - Setup() + th := Setup().InitBasic() + th.BasicClient.Logout() + Client := th.BasicClient - team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - rteam, _ := Client.CreateTeam(&team) + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + rteam, _ := Client.CreateTeam(team) + team = rteam.Data.(*model.Team) - user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} + user := model.User{Email: "success+" + model.NewId() + "@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} ruser, _ := Client.CreateUser(&user, "") + LinkUserToTeam(ruser.Data.(*model.User), rteam.Data.(*model.Team)) store.Must(Srv.Store.User().VerifyEmail(ruser.Data.(*model.User).Id)) Client.LoginByEmail(team.Name, user.Email, user.Password) + Client.SetTeamId(team.Id) if result, err := Client.GetMyTeam(""); err != nil { - t.Fatal("Failed to get user") + t.Fatal(err) } else { if result.Data.(*model.Team).DisplayName != team.DisplayName { t.Fatal("team names did not match") @@ -309,3 +491,14 @@ func TestGetMyTeam(t *testing.T) { } } } + +func TestGetTeamMembers(t *testing.T) { + th := Setup().InitBasic() + + if result, err := th.BasicClient.GetTeamMembers(th.BasicTeam.Id); err != nil { + t.Fatal(err) + } else { + members := result.Data.([]*model.TeamMember) + t.Log(members) + } +} diff --git a/api/user.go b/api/user.go index 08d096c51..16ba45dc4 100644 --- a/api/user.go +++ b/api/user.go @@ -5,8 +5,6 @@ package api import ( "bytes" - "crypto/tls" - b64 "encoding/base64" "fmt" l4g "github.com/alecthomas/log4go" "github.com/disintegration/imaging" @@ -34,45 +32,44 @@ import ( "time" ) -func InitUser(r *mux.Router) { +func InitUser() { l4g.Debug(utils.T("api.user.init.debug")) - sr := r.PathPrefix("/users").Subrouter() - sr.Handle("/create", ApiAppHandler(createUser)).Methods("POST") - sr.Handle("/update", ApiUserRequired(updateUser)).Methods("POST") - sr.Handle("/update_roles", ApiUserRequired(updateRoles)).Methods("POST") - sr.Handle("/update_active", ApiUserRequired(updateActive)).Methods("POST") - sr.Handle("/update_notify", ApiUserRequired(updateUserNotify)).Methods("POST") - sr.Handle("/newpassword", ApiUserRequired(updatePassword)).Methods("POST") - sr.Handle("/send_password_reset", ApiAppHandler(sendPasswordReset)).Methods("POST") - sr.Handle("/reset_password", ApiAppHandler(resetPassword)).Methods("POST") - sr.Handle("/login", ApiAppHandler(login)).Methods("POST") - sr.Handle("/logout", ApiUserRequired(logout)).Methods("POST") - sr.Handle("/login_ldap", ApiAppHandler(loginLdap)).Methods("POST") - sr.Handle("/revoke_session", ApiUserRequired(revokeSession)).Methods("POST") - sr.Handle("/attach_device", ApiUserRequired(attachDeviceId)).Methods("POST") - sr.Handle("/verify_email", ApiAppHandler(verifyEmail)).Methods("POST") - sr.Handle("/resend_verification", ApiAppHandler(resendVerification)).Methods("POST") - sr.Handle("/mfa", ApiAppHandler(checkMfa)).Methods("POST") - sr.Handle("/generate_mfa_qr", ApiUserRequiredTrustRequester(generateMfaQrCode)).Methods("GET") - sr.Handle("/update_mfa", ApiUserRequired(updateMfa)).Methods("POST") - - sr.Handle("/newimage", ApiUserRequired(uploadProfileImage)).Methods("POST") - - sr.Handle("/me", ApiAppHandler(getMe)).Methods("GET") - sr.Handle("/me_logged_in", ApiAppHandler(getMeLoggedIn)).Methods("GET") - sr.Handle("/status", ApiUserRequiredActivity(getStatuses, false)).Methods("POST") - sr.Handle("/profiles", ApiUserRequired(getProfiles)).Methods("GET") - sr.Handle("/profiles/{id:[A-Za-z0-9]+}", ApiUserRequired(getProfiles)).Methods("GET") - sr.Handle("/{id:[A-Za-z0-9]+}", ApiUserRequired(getUser)).Methods("GET") - sr.Handle("/{id:[A-Za-z0-9]+}/sessions", ApiUserRequired(getSessions)).Methods("GET") - sr.Handle("/{id:[A-Za-z0-9]+}/audits", ApiUserRequired(getAudits)).Methods("GET") - sr.Handle("/{id:[A-Za-z0-9]+}/image", ApiUserRequiredTrustRequester(getProfileImage)).Methods("GET") - - sr.Handle("/claim/email_to_oauth", ApiAppHandler(emailToOAuth)).Methods("POST") - sr.Handle("/claim/oauth_to_email", ApiUserRequired(oauthToEmail)).Methods("POST") - sr.Handle("/claim/email_to_ldap", ApiAppHandler(emailToLdap)).Methods("POST") - sr.Handle("/claim/ldap_to_email", ApiAppHandler(ldapToEmail)).Methods("POST") + BaseRoutes.Users.Handle("/create", ApiAppHandler(createUser)).Methods("POST") + BaseRoutes.Users.Handle("/update", ApiUserRequired(updateUser)).Methods("POST") + BaseRoutes.Users.Handle("/update_roles", ApiUserRequired(updateRoles)).Methods("POST") + BaseRoutes.Users.Handle("/update_active", ApiUserRequired(updateActive)).Methods("POST") + BaseRoutes.Users.Handle("/update_notify", ApiUserRequired(updateUserNotify)).Methods("POST") + BaseRoutes.Users.Handle("/newpassword", ApiUserRequired(updatePassword)).Methods("POST") + BaseRoutes.Users.Handle("/send_password_reset", ApiAppHandler(sendPasswordReset)).Methods("POST") + BaseRoutes.Users.Handle("/reset_password", ApiAppHandler(resetPassword)).Methods("POST") + BaseRoutes.Users.Handle("/login", ApiAppHandler(login)).Methods("POST") + BaseRoutes.Users.Handle("/logout", ApiAppHandler(logout)).Methods("POST") + BaseRoutes.Users.Handle("/login_ldap", ApiAppHandler(loginLdap)).Methods("POST") + BaseRoutes.Users.Handle("/revoke_session", ApiUserRequired(revokeSession)).Methods("POST") + BaseRoutes.Users.Handle("/attach_device", ApiUserRequired(attachDeviceId)).Methods("POST") + BaseRoutes.Users.Handle("/verify_email", ApiAppHandler(verifyEmail)).Methods("POST") + BaseRoutes.Users.Handle("/resend_verification", ApiAppHandler(resendVerification)).Methods("POST") + BaseRoutes.Users.Handle("/newimage", ApiUserRequired(uploadProfileImage)).Methods("POST") + BaseRoutes.Users.Handle("/me", ApiAppHandler(getMe)).Methods("GET") + BaseRoutes.Users.Handle("/initial_load", ApiAppHandler(getInitialLoad)).Methods("GET") + BaseRoutes.Users.Handle("/status", ApiUserRequiredActivity(getStatuses, false)).Methods("POST") + BaseRoutes.Users.Handle("/direct_profiles", ApiUserRequired(getDirectProfiles)).Methods("GET") + BaseRoutes.Users.Handle("/profiles/{id:[A-Za-z0-9]+}", ApiUserRequired(getProfiles)).Methods("GET") + + BaseRoutes.Users.Handle("/mfa", ApiAppHandler(checkMfa)).Methods("POST") + BaseRoutes.Users.Handle("/generate_mfa_qr", ApiUserRequiredTrustRequester(generateMfaQrCode)).Methods("GET") + BaseRoutes.Users.Handle("/update_mfa", ApiUserRequired(updateMfa)).Methods("POST") + + BaseRoutes.Users.Handle("/claim/email_to_oauth", ApiAppHandler(emailToOAuth)).Methods("POST") + BaseRoutes.Users.Handle("/claim/oauth_to_email", ApiUserRequired(oauthToEmail)).Methods("POST") + BaseRoutes.Users.Handle("/claim/email_to_ldap", ApiAppHandler(emailToLdap)).Methods("POST") + BaseRoutes.Users.Handle("/claim/ldap_to_email", ApiAppHandler(ldapToEmail)).Methods("POST") + + BaseRoutes.NeedUser.Handle("/get", ApiUserRequired(getUser)).Methods("GET") + BaseRoutes.NeedUser.Handle("/sessions", ApiUserRequired(getSessions)).Methods("GET") + BaseRoutes.NeedUser.Handle("/audits", ApiUserRequired(getAudits)).Methods("GET") + BaseRoutes.NeedUser.Handle("/image", ApiUserRequiredTrustRequester(getProfileImage)).Methods("GET") } func createUser(c *Context, w http.ResponseWriter, r *http.Request) { @@ -89,24 +86,13 @@ func createUser(c *Context, w http.ResponseWriter, r *http.Request) { return } - // the user's username is checked to be valid when they are saved to the database - - user.EmailVerified = false - - var team *model.Team - - if result := <-Srv.Store.Team().Get(user.TeamId); result.Err != nil { - c.Err = result.Err - return - } else { - team = result.Data.(*model.Team) - } - hash := r.URL.Query().Get("h") - + teamId := "" + var team *model.Team sendWelcomeEmail := true + user.EmailVerified = false - if IsVerifyHashRequired(user, team, hash) { + if len(hash) > 0 { data := r.URL.Query().Get("d") props := model.MapFromJson(strings.NewReader(data)) @@ -121,9 +107,14 @@ func createUser(c *Context, w http.ResponseWriter, r *http.Request) { return } - if user.TeamId != props["id"] { - c.Err = model.NewLocAppError("createUser", "api.user.create_user.team_name.app_error", nil, data) + teamId = props["id"] + + // try to load the team to make sure it exists + if result := <-Srv.Store.Team().Get(teamId); result.Err != nil { + c.Err = result.Err return + } else { + team = result.Data.(*model.Team) } user.Email = props["email"] @@ -131,8 +122,33 @@ func createUser(c *Context, w http.ResponseWriter, r *http.Request) { sendWelcomeEmail = false } - if user.IsSSOUser() { - user.EmailVerified = true + inviteId := r.URL.Query().Get("iid") + if len(inviteId) > 0 { + if result := <-Srv.Store.Team().GetByInviteId(inviteId); result.Err != nil { + c.Err = result.Err + return + } else { + team = result.Data.(*model.Team) + teamId = team.Id + } + } + + firstAccount := false + if sessionCache.Len() == 0 { + if cr := <-Srv.Store.User().GetTotalUsersCount(); cr.Err != nil { + c.Err = cr.Err + return + } else { + count := cr.Data.(int64) + if count <= 0 { + firstAccount = true + } + } + } + + if !firstAccount && !*utils.Cfg.TeamSettings.EnableOpenServer && len(teamId) == 0 { + c.Err = model.NewLocAppError("createUser", "api.user.create_user.no_open_server", nil, "email="+user.Email) + return } if !CheckUserDomain(user, utils.Cfg.TeamSettings.RestrictCreationToDomains) { @@ -140,14 +156,24 @@ func createUser(c *Context, w http.ResponseWriter, r *http.Request) { return } - ruser, err := CreateUser(team, user) + ruser, err := CreateUser(user) if err != nil { c.Err = err return } + if len(teamId) > 0 { + err := JoinUserToTeam(team, ruser) + if err != nil { + c.Err = err + return + } + + addDirectChannelsAndForget(team.Id, ruser) + } + if sendWelcomeEmail { - sendWelcomeEmailAndForget(c, ruser.Id, ruser.Email, team.Name, team.DisplayName, c.GetSiteURL(), c.GetTeamURLFromTeam(team), ruser.EmailVerified) + sendWelcomeEmailAndForget(c, ruser.Id, ruser.Email, c.GetSiteURL(), ruser.EmailVerified) } w.Write([]byte(ruser.ToJson())) @@ -196,26 +222,19 @@ func IsVerifyHashRequired(user *model.User, team *model.Team, hash string) bool return shouldVerifyHash } -func CreateUser(team *model.Team, user *model.User) (*model.User, *model.AppError) { +func CreateUser(user *model.User) (*model.User, *model.AppError) { - channelRole := "" - if team.Email == user.Email { - user.Roles = model.ROLE_TEAM_ADMIN - channelRole = model.CHANNEL_ROLE_ADMIN - - // Below is a speical case where the first user in the entire - // system is granted the system_admin role instead of admin - if result := <-Srv.Store.User().GetTotalUsersCount(); result.Err != nil { - return nil, result.Err - } else { - count := result.Data.(int64) - if count <= 0 { - user.Roles = model.ROLE_SYSTEM_ADMIN - } - } + user.Roles = "" + // Below is a speical case where the first user in the entire + // system is granted the system_admin role instead of admin + if result := <-Srv.Store.User().GetTotalUsersCount(); result.Err != nil { + return nil, result.Err } else { - user.Roles = "" + count := result.Data.(int64) + if count <= 0 { + user.Roles = model.ROLE_SYSTEM_ADMIN + } } user.MakeNonNil() @@ -226,13 +245,6 @@ func CreateUser(team *model.Team, user *model.User) (*model.User, *model.AppErro } else { ruser := result.Data.(*model.User) - // Soft error if there is an issue joining the default channels - if err := JoinDefaultChannels(ruser, channelRole); err != nil { - l4g.Error(utils.T("api.user.create_user.joining.error"), ruser.Id, ruser.TeamId, err) - } - - addDirectChannelsAndForget(ruser) - if user.EmailVerified { if cresult := <-Srv.Store.User().VerifyEmail(ruser.Id); cresult.Err != nil { l4g.Error(utils.T("api.user.create_user.verified.error"), cresult.Err) @@ -247,15 +259,13 @@ func CreateUser(team *model.Team, user *model.User) (*model.User, *model.AppErro ruser.Sanitize(map[string]bool{}) // This message goes to every channel, so the channelId is irrelevant - message := model.NewMessage(team.Id, "", ruser.Id, model.ACTION_NEW_USER) - - PublishAndForget(message) + PublishAndForget(model.NewMessage("", "", ruser.Id, model.ACTION_NEW_USER)) return ruser, nil } } -func CreateOAuthUser(c *Context, w http.ResponseWriter, r *http.Request, service string, userData io.Reader, team *model.Team) *model.User { +func CreateOAuthUser(c *Context, w http.ResponseWriter, r *http.Request, service string, userData io.Reader, teamId string) *model.User { var user *model.User provider := einterfaces.GetOauthProvider(service) if provider == nil { @@ -270,49 +280,59 @@ func CreateOAuthUser(c *Context, w http.ResponseWriter, r *http.Request, service return nil } - suchan := Srv.Store.User().GetByAuth(team.Id, user.AuthData, service) - euchan := Srv.Store.User().GetByEmail(team.Id, user.Email) + suchan := Srv.Store.User().GetByAuth(user.AuthData, service) + euchan := Srv.Store.User().GetByEmail(user.Email) - if team.Email == "" { - team.Email = user.Email - if result := <-Srv.Store.Team().Update(team); result.Err != nil { - c.Err = result.Err - return nil - } - } else { - found := true - count := 0 - for found { - if found = IsUsernameTaken(user.Username, team.Id); c.Err != nil { - return nil - } else if found { - user.Username = user.Username + strconv.Itoa(count) - count += 1 - } + var tchan store.StoreChannel + if len(teamId) != 0 { + tchan = Srv.Store.Team().Get(teamId) + } + + found := true + count := 0 + for found { + if found = IsUsernameTaken(user.Username); found { + user.Username = user.Username + strconv.Itoa(count) + count += 1 } } if result := <-suchan; result.Err == nil { - c.Err = model.NewLocAppError("signupCompleteOAuth", "api.user.create_oauth_user.already_used.app_error", - map[string]interface{}{"Service": service, "DisplayName": team.DisplayName}, "email="+user.Email) + c.Err = model.NewLocAppError("CreateOAuthUser", "api.user.create_oauth_user.already_used.app_error", + map[string]interface{}{"Service": service}, "email="+user.Email) return nil } if result := <-euchan; result.Err == nil { - c.Err = model.NewLocAppError("signupCompleteOAuth", "api.user.create_oauth_user.already_attached.app_error", - map[string]interface{}{"Service": service, "DisplayName": team.DisplayName}, "email="+user.Email) + c.Err = model.NewLocAppError("CreateOAuthUser", "api.user.create_oauth_user.already_attached.app_error", + map[string]interface{}{"Service": service}, "email="+user.Email) return nil } - user.TeamId = team.Id user.EmailVerified = true - ruser, err := CreateUser(team, user) + ruser, err := CreateUser(user) if err != nil { c.Err = err return nil } + if tchan != nil { + if result := <-tchan; result.Err != nil { + c.Err = result.Err + return nil + } else { + team := result.Data.(*model.Team) + err = JoinUserToTeam(team, user) + if err != nil { + c.Err = err + return nil + } + + addDirectChannelsAndForget(team.Id, user) + } + } + Login(c, w, r, ruser, "") if c.Err != nil { return nil @@ -321,23 +341,23 @@ func CreateOAuthUser(c *Context, w http.ResponseWriter, r *http.Request, service return ruser } -func sendWelcomeEmailAndForget(c *Context, userId, email, teamName, teamDisplayName, siteURL, teamURL string, verified bool) { +func sendWelcomeEmailAndForget(c *Context, userId string, email string, siteURL string, verified bool) { go func() { subjectPage := utils.NewHTMLTemplate("welcome_subject", c.Locale) - subjectPage.Props["Subject"] = c.T("api.templates.welcome_subject", map[string]interface{}{"TeamDisplayName": teamDisplayName}) + subjectPage.Props["Subject"] = c.T("api.templates.welcome_subject", map[string]interface{}{"TeamDisplayName": siteURL}) bodyPage := utils.NewHTMLTemplate("welcome_body", c.Locale) bodyPage.Props["SiteURL"] = siteURL - bodyPage.Props["Title"] = c.T("api.templates.welcome_body.title", map[string]interface{}{"TeamDisplayName": teamDisplayName}) + bodyPage.Props["Title"] = c.T("api.templates.welcome_body.title", map[string]interface{}{"TeamDisplayName": siteURL}) bodyPage.Props["Info"] = c.T("api.templates.welcome_body.info") bodyPage.Props["Button"] = c.T("api.templates.welcome_body.button") bodyPage.Props["Info2"] = c.T("api.templates.welcome_body.info2") bodyPage.Props["Info3"] = c.T("api.templates.welcome_body.info3") - bodyPage.Props["TeamURL"] = teamURL + bodyPage.Props["TeamURL"] = siteURL if !verified { - link := fmt.Sprintf("%s/do_verify_email?uid=%s&hid=%s&teamname=%s&email=%s", siteURL, userId, model.HashPassword(userId), teamName, email) + link := fmt.Sprintf("%s/do_verify_email?uid=%s&hid=%s&email=%s", siteURL, userId, model.HashPassword(userId), email) bodyPage.Props["VerifyUrl"] = link } @@ -347,11 +367,11 @@ func sendWelcomeEmailAndForget(c *Context, userId, email, teamName, teamDisplayN }() } -func addDirectChannelsAndForget(user *model.User) { +func addDirectChannelsAndForget(teamId string, user *model.User) { go func() { var profiles map[string]*model.User - if result := <-Srv.Store.User().GetProfiles(user.TeamId); result.Err != nil { - l4g.Error(utils.T("api.user.add_direct_channels_and_forget.failed.error"), user.Id, user.TeamId, result.Err.Error()) + if result := <-Srv.Store.User().GetProfiles(teamId); result.Err != nil { + l4g.Error(utils.T("api.user.add_direct_channels_and_forget.failed.error"), user.Id, teamId, result.Err.Error()) return } else { profiles = result.Data.(map[string]*model.User) @@ -381,23 +401,23 @@ func addDirectChannelsAndForget(user *model.User) { } if result := <-Srv.Store.Preference().Save(&preferences); result.Err != nil { - l4g.Error(utils.T("api.user.add_direct_channels_and_forget.failed.error"), user.Id, user.TeamId, result.Err.Error()) + l4g.Error(utils.T("api.user.add_direct_channels_and_forget.failed.error"), user.Id, teamId, result.Err.Error()) } }() } -func SendVerifyEmailAndForget(c *Context, userId, userEmail, teamName, teamDisplayName, siteURL, teamURL string) { +func SendVerifyEmailAndForget(c *Context, userId, userEmail, siteURL string) { go func() { - link := fmt.Sprintf("%s/do_verify_email?uid=%s&hid=%s&teamname=%s&email=%s", siteURL, userId, model.HashPassword(userId), teamName, userEmail) + link := fmt.Sprintf("%s/do_verify_email?uid=%s&hid=%s&email=%s", siteURL, userId, model.HashPassword(userId), userEmail) subjectPage := utils.NewHTMLTemplate("verify_subject", c.Locale) subjectPage.Props["Subject"] = c.T("api.templates.verify_subject", - map[string]interface{}{"TeamDisplayName": teamDisplayName, "SiteName": utils.ClientCfg["SiteName"]}) + map[string]interface{}{"TeamDisplayName": utils.ClientCfg["SiteName"], "SiteName": utils.ClientCfg["SiteName"]}) bodyPage := utils.NewHTMLTemplate("verify_body", c.Locale) bodyPage.Props["SiteURL"] = siteURL - bodyPage.Props["Title"] = c.T("api.templates.verify_body.title", map[string]interface{}{"TeamDisplayName": teamDisplayName}) + bodyPage.Props["Title"] = c.T("api.templates.verify_body.title", map[string]interface{}{"TeamDisplayName": utils.ClientCfg["SiteName"]}) bodyPage.Props["Info"] = c.T("api.templates.verify_body.info") bodyPage.Props["VerifyUrl"] = link bodyPage.Props["Button"] = c.T("api.templates.verify_body.button") @@ -408,6 +428,54 @@ func SendVerifyEmailAndForget(c *Context, userId, userEmail, teamName, teamDispl }() } +func login(c *Context, w http.ResponseWriter, r *http.Request) { + props := model.MapFromJson(r.Body) + + if len(props["password"]) == 0 { + c.Err = model.NewLocAppError("login", "api.user.login.blank_pwd.app_error", nil, "") + c.Err.StatusCode = http.StatusBadRequest + return + } + + var user *model.User + if len(props["id"]) != 0 { + user = LoginById(c, w, r, props["id"], props["password"], props["token"], props["device_id"]) + } else if len(props["email"]) != 0 { + user = LoginByEmail(c, w, r, props["email"], props["name"], props["password"], props["token"], props["device_id"]) + } else if len(props["username"]) != 0 { + user = LoginByUsername(c, w, r, props["username"], props["name"], props["password"], props["token"], props["device_id"]) + } else { + c.Err = model.NewLocAppError("login", "api.user.login.not_provided.app_error", nil, "") + c.Err.StatusCode = http.StatusBadRequest + return + } + + if c.Err != nil { + return + } + + if user != nil { + user.Sanitize(map[string]bool{}) + } else { + user = &model.User{} + } + w.Write([]byte(user.ToJson())) +} + +func doUserPasswordAuthenticationAndLogin(c *Context, w http.ResponseWriter, r *http.Request, user *model.User, password string, mfaToken string, deviceId string) bool { + c.LogAuditWithUserId(user.Id, "attempt") + if err := checkPasswordAndAllCriteria(user, password, mfaToken); err != nil { + c.LogAuditWithUserId(user.Id, "fail") + c.Err = err + c.Err.StatusCode = http.StatusUnauthorized + return false + } else { + Login(c, w, r, user, deviceId) + c.LogAuditWithUserId(user.Id, "success") + return true + } +} + func LoginById(c *Context, w http.ResponseWriter, r *http.Request, userId, password, mfaToken, deviceId string) *model.User { if result := <-Srv.Store.User().Get(userId); result.Err != nil { c.Err = result.Err @@ -415,8 +483,13 @@ func LoginById(c *Context, w http.ResponseWriter, r *http.Request, userId, passw } else { user := result.Data.(*model.User) - if authenticateUserPasswordAndToken(c, user, password, mfaToken) { - Login(c, w, r, user, deviceId) + if len(user.AuthData) != 0 { + c.Err = model.NewLocAppError("LoginById", "api.user.login_by_email.sign_in.app_error", + map[string]interface{}{"AuthService": user.AuthService}, "") + return nil + } + + if doUserPasswordAuthenticationAndLogin(c, w, r, user, password, mfaToken, deviceId) { return user } } @@ -425,18 +498,9 @@ func LoginById(c *Context, w http.ResponseWriter, r *http.Request, userId, passw } func LoginByEmail(c *Context, w http.ResponseWriter, r *http.Request, email, name, password, mfaToken, deviceId string) *model.User { - var team *model.Team - - if result := <-Srv.Store.Team().GetByName(name); result.Err != nil { + if result := <-Srv.Store.User().GetByEmail(email); result.Err != nil { c.Err = result.Err - return nil - } else { - team = result.Data.(*model.Team) - } - - if result := <-Srv.Store.User().GetByEmail(team.Id, email); result.Err != nil { - c.Err = result.Err - c.Err.StatusCode = http.StatusForbidden + c.Err.StatusCode = http.StatusUnauthorized return nil } else { user := result.Data.(*model.User) @@ -447,8 +511,7 @@ func LoginByEmail(c *Context, w http.ResponseWriter, r *http.Request, email, nam return nil } - if authenticateUserPasswordAndToken(c, user, password, mfaToken) { - Login(c, w, r, user, deviceId) + if doUserPasswordAuthenticationAndLogin(c, w, r, user, password, mfaToken, deviceId) { return user } } @@ -457,18 +520,9 @@ func LoginByEmail(c *Context, w http.ResponseWriter, r *http.Request, email, nam } func LoginByUsername(c *Context, w http.ResponseWriter, r *http.Request, username, name, password, mfaToken, deviceId string) *model.User { - var team *model.Team - - if result := <-Srv.Store.Team().GetByName(name); result.Err != nil { + if result := <-Srv.Store.User().GetByUsername(username); result.Err != nil { c.Err = result.Err - return nil - } else { - team = result.Data.(*model.Team) - } - - if result := <-Srv.Store.User().GetByUsername(team.Id, username); result.Err != nil { - c.Err = result.Err - c.Err.StatusCode = http.StatusForbidden + c.Err.StatusCode = http.StatusUnauthorized return nil } else { user := result.Data.(*model.User) @@ -479,8 +533,7 @@ func LoginByUsername(c *Context, w http.ResponseWriter, r *http.Request, usernam return nil } - if authenticateUserPasswordAndToken(c, user, password, mfaToken) { - Login(c, w, r, user, deviceId) + if doUserPasswordAuthenticationAndLogin(c, w, r, user, password, mfaToken, deviceId) { return user } } @@ -488,7 +541,7 @@ func LoginByUsername(c *Context, w http.ResponseWriter, r *http.Request, usernam return nil } -func LoginByOAuth(c *Context, w http.ResponseWriter, r *http.Request, service string, userData io.Reader, team *model.Team) *model.User { +func LoginByOAuth(c *Context, w http.ResponseWriter, r *http.Request, service string, userData io.Reader) *model.User { buf := bytes.Buffer{} buf.ReadFrom(userData) @@ -509,9 +562,9 @@ func LoginByOAuth(c *Context, w http.ResponseWriter, r *http.Request, service st } var user *model.User - if result := <-Srv.Store.User().GetByAuth(team.Id, authData, service); result.Err != nil { - if result.Err.Id == store.MISSING_AUTH_ACCOUNT_ERROR && team.AllowOpenInvite { - return CreateOAuthUser(c, w, r, service, bytes.NewReader(buf.Bytes()), team) + if result := <-Srv.Store.User().GetByAuth(authData, service); result.Err != nil { + if result.Err.Id == store.MISSING_AUTH_ACCOUNT_ERROR { + return CreateOAuthUser(c, w, r, service, bytes.NewReader(buf.Bytes()), "") } c.Err = result.Err return nil @@ -522,81 +575,77 @@ func LoginByOAuth(c *Context, w http.ResponseWriter, r *http.Request, service st } } -func authenticateUserPasswordAndToken(c *Context, user *model.User, password string, token string) bool { - return checkUserLoginAttempts(c, user) && checkUserMfa(c, user, token) && checkUserPassword(c, user, password) -} - -func checkUserLoginAttempts(c *Context, user *model.User) bool { - if user.FailedAttempts >= utils.Cfg.ServiceSettings.MaximumLoginAttempts { - c.LogAuditWithUserId(user.Id, "fail") - c.Err = model.NewLocAppError("checkUserLoginAttempts", "api.user.check_user_login_attempts.too_many.app_error", nil, "user_id="+user.Id) - c.Err.StatusCode = http.StatusForbidden - return false +func loginLdap(c *Context, w http.ResponseWriter, r *http.Request) { + if !*utils.Cfg.LdapSettings.Enable { + c.Err = model.NewLocAppError("loginLdap", "api.user.login_ldap.disabled.app_error", nil, "") + c.Err.StatusCode = http.StatusNotImplemented + return } - return true -} - -func checkUserPassword(c *Context, user *model.User, password string) bool { - if !model.ComparePassword(user.Password, password) { - c.LogAuditWithUserId(user.Id, "fail") - c.Err = model.NewLocAppError("checkUserPassword", "api.user.check_user_password.invalid.app_error", nil, "user_id="+user.Id) - c.Err.StatusCode = http.StatusForbidden - - if result := <-Srv.Store.User().UpdateFailedPasswordAttempts(user.Id, user.FailedAttempts+1); result.Err != nil { - c.LogError(result.Err) - } + props := model.MapFromJson(r.Body) - return false - } else { - if result := <-Srv.Store.User().UpdateFailedPasswordAttempts(user.Id, 0); result.Err != nil { - c.LogError(result.Err) - } + password := props["password"] + id := props["id"] + mfaToken := props["token"] - return true + if len(password) == 0 { + c.Err = model.NewLocAppError("loginLdap", "api.user.login_ldap.blank_pwd.app_error", nil, "") + c.Err.StatusCode = http.StatusBadRequest + return } -} -func checkUserMfa(c *Context, user *model.User, token string) bool { - if !user.MfaActive || !utils.IsLicensed || !*utils.License.Features.MFA || !*utils.Cfg.ServiceSettings.EnableMultifactorAuthentication { - return true + if len(id) == 0 { + c.Err = model.NewLocAppError("loginLdap", "api.user.login_ldap.need_id.app_error", nil, "") + c.Err.StatusCode = http.StatusBadRequest + return } - mfaInterface := einterfaces.GetMfaInterface() - if mfaInterface == nil { - c.Err = model.NewLocAppError("checkUserMfa", "api.user.check_user_mfa.not_available.app_error", nil, "") + ldapInterface := einterfaces.GetLdapInterface() + if ldapInterface == nil { + c.Err = model.NewLocAppError("loginLdap", "api.user.login_ldap.not_available.app_error", nil, "") c.Err.StatusCode = http.StatusNotImplemented - return false + return } - if ok, err := mfaInterface.ValidateToken(user.MfaSecret, token); err != nil { + user, err := ldapInterface.DoLogin(id, password) + if err != nil { + if user != nil { + c.LogAuditWithUserId(user.Id, "attempt") + c.LogAuditWithUserId(user.Id, "fail") + } else { + c.LogAudit("attempt") + c.LogAudit("fail") + } c.Err = err - return false - } else if !ok { - c.Err = model.NewLocAppError("checkUserMfa", "api.user.check_user_mfa.bad_code.app_error", nil, "") - return false - } else { - return true + c.Err.StatusCode = http.StatusUnauthorized + return } -} - -// User MUST be validated before calling Login -func Login(c *Context, w http.ResponseWriter, r *http.Request, user *model.User, deviceId string) { c.LogAuditWithUserId(user.Id, "attempt") - if !user.EmailVerified && utils.Cfg.EmailSettings.RequireEmailVerification { - c.Err = model.NewLocAppError("Login", "api.user.login.not_verified.app_error", nil, "user_id="+user.Id) - c.Err.StatusCode = http.StatusForbidden + if err = checkUserAdditionalAuthenticationCriteria(user, mfaToken); err != nil { + c.LogAuditWithUserId(user.Id, "fail") + c.Err = err + c.Err.StatusCode = http.StatusUnauthorized return } - if user.DeleteAt > 0 { - c.Err = model.NewLocAppError("Login", "api.user.login.inactive.app_error", nil, "user_id="+user.Id) - c.Err.StatusCode = http.StatusForbidden - return + // User is authenticated at this point + + Login(c, w, r, user, props["device_id"]) + c.LogAuditWithUserId(user.Id, "success") + + if user != nil { + user.Sanitize(map[string]bool{}) + } else { + user = &model.User{} } + w.Write([]byte(user.ToJson())) +} - session := &model.Session{UserId: user.Id, TeamId: user.TeamId, Roles: user.Roles, DeviceId: deviceId, IsOAuth: false} +// User MUST be authenticated completely before calling Login +func Login(c *Context, w http.ResponseWriter, r *http.Request, user *model.User, deviceId string) { + + session := &model.Session{UserId: user.Id, Roles: user.Roles, DeviceId: deviceId, IsOAuth: false} maxAge := *utils.Cfg.ServiceSettings.SessionLengthWebInDays * 60 * 60 * 24 @@ -607,7 +656,7 @@ func Login(c *Context, w http.ResponseWriter, r *http.Request, user *model.User, // A special case where we logout of all other sessions with the same Id if result := <-Srv.Store.Session().GetSessions(user.Id); result.Err != nil { c.Err = result.Err - c.Err.StatusCode = http.StatusForbidden + c.Err.StatusCode = http.StatusInternalServerError return } else { sessions := result.Data.([]*model.Session) @@ -653,7 +702,7 @@ func Login(c *Context, w http.ResponseWriter, r *http.Request, user *model.User, if result := <-Srv.Store.Session().Save(session); result.Err != nil { c.Err = result.Err - c.Err.StatusCode = http.StatusForbidden + c.Err.StatusCode = http.StatusInternalServerError return } else { session = result.Data.(*model.Session) @@ -675,110 +724,6 @@ func Login(c *Context, w http.ResponseWriter, r *http.Request, user *model.User, http.SetCookie(w, sessionCookie) c.Session = *session - c.LogAuditWithUserId(user.Id, "success") -} - -func login(c *Context, w http.ResponseWriter, r *http.Request) { - props := model.MapFromJson(r.Body) - - if len(props["password"]) == 0 { - c.Err = model.NewLocAppError("login", "api.user.login.blank_pwd.app_error", nil, "") - c.Err.StatusCode = http.StatusForbidden - return - } - - var user *model.User - if len(props["id"]) != 0 { - user = LoginById(c, w, r, props["id"], props["password"], props["token"], props["device_id"]) - } else if len(props["email"]) != 0 && len(props["name"]) != 0 { - user = LoginByEmail(c, w, r, props["email"], props["name"], props["password"], props["token"], props["device_id"]) - } else if len(props["username"]) != 0 && len(props["name"]) != 0 { - user = LoginByUsername(c, w, r, props["username"], props["name"], props["password"], props["token"], props["device_id"]) - } else { - c.Err = model.NewLocAppError("login", "api.user.login.not_provided.app_error", nil, "") - c.Err.StatusCode = http.StatusForbidden - return - } - - if c.Err != nil { - return - } - - if user != nil { - user.Sanitize(map[string]bool{}) - } else { - user = &model.User{} - } - w.Write([]byte(user.ToJson())) -} - -func loginLdap(c *Context, w http.ResponseWriter, r *http.Request) { - if !*utils.Cfg.LdapSettings.Enable { - c.Err = model.NewLocAppError("loginLdap", "api.user.login_ldap.disabled.app_error", nil, "") - c.Err.StatusCode = http.StatusNotImplemented - return - } - - props := model.MapFromJson(r.Body) - - password := props["password"] - id := props["id"] - teamName := props["teamName"] - mfaToken := props["token"] - - if len(password) == 0 { - c.Err = model.NewLocAppError("loginLdap", "api.user.login_ldap.blank_pwd.app_error", nil, "") - c.Err.StatusCode = http.StatusForbidden - return - } - - if len(id) == 0 { - c.Err = model.NewLocAppError("loginLdap", "api.user.login_ldap.need_id.app_error", nil, "") - c.Err.StatusCode = http.StatusForbidden - return - } - - teamc := Srv.Store.Team().GetByName(teamName) - - ldapInterface := einterfaces.GetLdapInterface() - if ldapInterface == nil { - c.Err = model.NewLocAppError("loginLdap", "api.user.login_ldap.not_available.app_error", nil, "") - c.Err.StatusCode = http.StatusNotImplemented - return - } - - var team *model.Team - if result := <-teamc; result.Err != nil { - c.Err = result.Err - return - } else { - team = result.Data.(*model.Team) - } - - user, err := ldapInterface.DoLogin(team, id, password) - if err != nil { - c.Err = err - return - } - - if !checkUserLoginAttempts(c, user) { - return - } - - if !checkUserMfa(c, user, mfaToken) { - return - } - - // User is authenticated at this point - - Login(c, w, r, user, props["device_id"]) - - if user != nil { - user.Sanitize(map[string]bool{}) - } else { - user = &model.User{} - } - w.Write([]byte(user.ToJson())) } func revokeSession(c *Context, w http.ResponseWriter, r *http.Request) { @@ -805,7 +750,7 @@ func attachDeviceId(c *Context, w http.ResponseWriter, r *http.Request) { // A special case where we logout of all other sessions with the same Id if result := <-Srv.Store.Session().GetSessions(c.Session.UserId); result.Err != nil { c.Err = result.Err - c.Err.StatusCode = http.StatusForbidden + c.Err.StatusCode = http.StatusInternalServerError return } else { sessions := result.Data.([]*model.Session) @@ -875,7 +820,7 @@ func RevokeAllSession(c *Context, userId string) { func getSessions(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) - id := params["id"] + id := params["user_id"] if !c.HasPermissionsToUser(id, "getSessions") { return @@ -907,9 +852,11 @@ func logout(c *Context, w http.ResponseWriter, r *http.Request) { func Logout(c *Context, w http.ResponseWriter, r *http.Request) { c.LogAudit("") c.RemoveSessionCookie(w, r) - if result := <-Srv.Store.Session().Remove(c.Session.Id); result.Err != nil { - c.Err = result.Err - return + if c.Session.Id != "" { + if result := <-Srv.Store.Session().Remove(c.Session.Id); result.Err != nil { + c.Err = result.Err + return + } } } @@ -934,29 +881,99 @@ func getMe(c *Context, w http.ResponseWriter, r *http.Request) { } } -func getMeLoggedIn(c *Context, w http.ResponseWriter, r *http.Request) { - data := make(map[string]string) - data["logged_in"] = "false" - data["team_name"] = "" +func getInitialLoad(c *Context, w http.ResponseWriter, r *http.Request) { + + il := model.InitialLoad{} + + var cchan store.StoreChannel + + if sessionCache.Len() == 0 { + // Below is a speical case when intializating a new server + // Lets check to make sure the server is really empty + + cchan = Srv.Store.User().GetTotalUsersCount() + } if len(c.Session.UserId) != 0 { - teamChan := Srv.Store.Team().Get(c.Session.TeamId) - var team *model.Team - if tr := <-teamChan; tr.Err != nil { - c.Err = tr.Err + uchan := Srv.Store.User().Get(c.Session.UserId) + pchan := Srv.Store.Preference().GetAll(c.Session.UserId) + tchan := Srv.Store.Team().GetTeamsByUserId(c.Session.UserId) + dpchan := Srv.Store.User().GetDirectProfiles(c.Session.UserId) + + il.TeamMembers = c.Session.TeamMembers + + if ru := <-uchan; ru.Err != nil { + c.Err = ru.Err + return + } else { + il.User = ru.Data.(*model.User) + il.User.Sanitize(map[string]bool{}) + } + + if rp := <-pchan; rp.Err != nil { + c.Err = rp.Err + return + } else { + il.Preferences = rp.Data.(model.Preferences) + } + + if rt := <-tchan; rt.Err != nil { + c.Err = rt.Err + return + } else { + il.Teams = rt.Data.([]*model.Team) + + for _, team := range il.Teams { + team.Sanitize() + } + } + + if dp := <-dpchan; dp.Err != nil { + c.Err = dp.Err return } else { - team = tr.Data.(*model.Team) + profiles := dp.Data.(map[string]*model.User) + + for k, p := range profiles { + options := utils.Cfg.GetSanitizeOptions() + options["passwordupdate"] = false + + if c.IsSystemAdmin() { + options["fullname"] = true + options["email"] = true + } else { + p.ClearNonProfileFields() + } + + p.Sanitize(options) + profiles[k] = p + } + + il.DirectProfiles = profiles } - data["logged_in"] = "true" - data["team_name"] = team.Name } - w.Write([]byte(model.MapToJson(data))) + + if cchan != nil { + if cr := <-cchan; cr.Err != nil { + c.Err = cr.Err + return + } else { + count := cr.Data.(int64) + if count <= 0 { + il.NoAccounts = true + } + } + } + + il.ClientCfg = utils.ClientCfg + il.LicenseCfg = utils.ClientLicense + + w.Write([]byte(il.ToJson())) } func getUser(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) - id := params["id"] + id := params["user_id"] if !c.HasPermissionsToUser(id, "getUser") { return @@ -977,17 +994,12 @@ func getUser(c *Context, w http.ResponseWriter, r *http.Request) { func getProfiles(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) - id, ok := params["id"] - if ok { - // You must be system admin to access another team - if id != c.Session.TeamId { - if !c.HasSystemAdminPermissions("getProfiles") { - return - } - } + id := params["id"] - } else { - id = c.Session.TeamId + if c.Session.GetTeamByTeamId(id) == nil { + if !c.HasSystemAdminPermissions("getProfiles") { + return + } } etag := (<-Srv.Store.User().GetEtagForProfiles(id)).Data.(string) @@ -1008,10 +1020,44 @@ func getProfiles(c *Context, w http.ResponseWriter, r *http.Request) { if c.IsSystemAdmin() { options["fullname"] = true options["email"] = true + } else { + p.ClearNonProfileFields() + } + + p.Sanitize(options) + profiles[k] = p + } + + w.Header().Set(model.HEADER_ETAG_SERVER, etag) + w.Write([]byte(model.UserMapToJson(profiles))) + return + } +} + +func getDirectProfiles(c *Context, w http.ResponseWriter, r *http.Request) { + etag := (<-Srv.Store.User().GetEtagForDirectProfiles(c.Session.UserId)).Data.(string) + if HandleEtag(etag, w, r) { + return + } + + if result := <-Srv.Store.User().GetDirectProfiles(c.Session.UserId); result.Err != nil { + c.Err = result.Err + return + } else { + profiles := result.Data.(map[string]*model.User) + + for k, p := range profiles { + options := utils.Cfg.GetSanitizeOptions() + options["passwordupdate"] = false + + if c.IsSystemAdmin() { + options["fullname"] = true + options["email"] = true + } else { + p.ClearNonProfileFields() } p.Sanitize(options) - p.ClearNonProfileFields() profiles[k] = p } @@ -1023,7 +1069,7 @@ func getProfiles(c *Context, w http.ResponseWriter, r *http.Request) { func getAudits(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) - id := params["id"] + id := params["user_id"] if !c.HasPermissionsToUser(id, "getAudits") { return @@ -1133,7 +1179,7 @@ func createProfileImage(username string, userId string) ([]byte, *model.AppError func getProfileImage(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) - id := params["id"] + id := params["user_id"] if result := <-Srv.Store.User().Get(id); result.Err != nil { c.Err = result.Err @@ -1148,7 +1194,7 @@ func getProfileImage(c *Context, w http.ResponseWriter, r *http.Request) { return } } else { - path := "teams/" + c.Session.TeamId + "/users/" + id + "/profile.png" + path := "/users/" + id + "/profile.png" if data, err := ReadFile(path); err != nil { @@ -1249,7 +1295,7 @@ func uploadProfileImage(c *Context, w http.ResponseWriter, r *http.Request) { return } - path := "teams/" + c.Session.TeamId + "/users/" + c.Session.UserId + "/profile.png" + path := "users/" + c.Session.UserId + "/profile.png" if err := WriteFile(buf.Bytes(), path); err != nil { c.Err = model.NewLocAppError("uploadProfileImage", "api.user.upload_profile_user.upload_profile.app_error", nil, "") @@ -1285,15 +1331,10 @@ func updateUser(c *Context, w http.ResponseWriter, r *http.Request) { rusers := result.Data.([2]*model.User) if rusers[0].Email != rusers[1].Email { - if tresult := <-Srv.Store.Team().Get(rusers[1].TeamId); tresult.Err != nil { - l4g.Error(tresult.Err.Message) - } else { - team := tresult.Data.(*model.Team) - sendEmailChangeEmailAndForget(c, rusers[1].Email, rusers[0].Email, team.DisplayName, c.GetTeamURLFromTeam(team), c.GetSiteURL()) + sendEmailChangeEmailAndForget(c, rusers[1].Email, rusers[0].Email, c.GetSiteURL()) - if utils.Cfg.EmailSettings.RequireEmailVerification { - SendEmailChangeVerifyEmailAndForget(c, rusers[0].Id, rusers[0].Email, team.Name, team.DisplayName, c.GetSiteURL(), c.GetTeamURLFromTeam(team)) - } + if utils.Cfg.EmailSettings.RequireEmailVerification { + SendEmailChangeVerifyEmailAndForget(c, rusers[0].Id, rusers[0].Email, c.GetSiteURL()) } } @@ -1346,12 +1387,10 @@ func updatePassword(c *Context, w http.ResponseWriter, r *http.Request) { user := result.Data.(*model.User) - tchan := Srv.Store.Team().Get(user.TeamId) - if user.AuthData != "" { c.LogAudit("failed - tried to update user password who was logged in through oauth") c.Err = model.NewLocAppError("updatePassword", "api.user.update_password.oauth.app_error", nil, "auth_service="+user.AuthService) - c.Err.StatusCode = http.StatusForbidden + c.Err.StatusCode = http.StatusBadRequest return } @@ -1363,17 +1402,11 @@ func updatePassword(c *Context, w http.ResponseWriter, r *http.Request) { if uresult := <-Srv.Store.User().UpdatePassword(c.Session.UserId, model.HashPassword(newPassword)); uresult.Err != nil { c.Err = model.NewLocAppError("updatePassword", "api.user.update_password.failed.app_error", nil, uresult.Err.Error()) - c.Err.StatusCode = http.StatusForbidden return } else { c.LogAudit("completed") - if tresult := <-tchan; tresult.Err != nil { - l4g.Error(tresult.Err.Message) - } else { - team := tresult.Data.(*model.Team) - sendPasswordChangeEmailAndForget(c, user.Email, team.DisplayName, c.GetTeamURLFromTeam(team), c.GetSiteURL(), c.T("api.user.update_password.menu")) - } + sendPasswordChangeEmailAndForget(c, user.Email, c.GetSiteURL(), c.T("api.user.update_password.menu")) data := make(map[string]string) data["user_id"] = uresult.Data.(string) @@ -1391,11 +1424,18 @@ func updateRoles(c *Context, w http.ResponseWriter, r *http.Request) { } new_roles := props["new_roles"] - if !model.IsValidRoles(new_roles) { + if !model.IsValidUserRoles(new_roles) { c.SetInvalidParam("updateRoles", "new_roles") return } + // If you are not the system admin then you can only demote yourself + if !c.IsSystemAdmin() && user_id != c.Session.UserId { + c.Err = model.NewLocAppError("updateRoles", "api.user.update_roles.system_admin_set.app_error", nil, "") + c.Err.StatusCode = http.StatusForbidden + } + + // Only another system admin can add the system admin role if model.IsInRole(new_roles, model.ROLE_SYSTEM_ADMIN) && !c.IsSystemAdmin() { c.Err = model.NewLocAppError("updateRoles", "api.user.update_roles.system_admin_set.app_error", nil, "") c.Err.StatusCode = http.StatusForbidden @@ -1410,22 +1450,6 @@ func updateRoles(c *Context, w http.ResponseWriter, r *http.Request) { user = result.Data.(*model.User) } - if !c.HasPermissionsToTeam(user.TeamId, "updateRoles") { - return - } - - if !c.IsTeamAdmin() { - c.Err = model.NewLocAppError("updateRoles", "api.user.update_roles.permissions.app_error", nil, "userId="+user_id) - c.Err.StatusCode = http.StatusForbidden - return - } - - if user.IsInRole(model.ROLE_SYSTEM_ADMIN) && !c.IsSystemAdmin() { - c.Err = model.NewLocAppError("updateRoles", "api.user.update_roles.system_admin_mod.app_error", nil, "") - c.Err.StatusCode = http.StatusForbidden - return - } - ruser := UpdateRoles(c, user, new_roles) if c.Err != nil { return @@ -1456,29 +1480,6 @@ func updateRoles(c *Context, w http.ResponseWriter, r *http.Request) { } func UpdateRoles(c *Context, user *model.User, roles string) *model.User { - // make sure there is at least 1 other active admin - - if !model.IsInRole(roles, model.ROLE_SYSTEM_ADMIN) { - if model.IsInRole(user.Roles, model.ROLE_TEAM_ADMIN) && !model.IsInRole(roles, model.ROLE_TEAM_ADMIN) { - if result := <-Srv.Store.User().GetProfiles(user.TeamId); result.Err != nil { - c.Err = result.Err - return nil - } else { - activeAdmins := -1 - profileUsers := result.Data.(map[string]*model.User) - for _, profileUser := range profileUsers { - if profileUser.DeleteAt == 0 && model.IsInRole(profileUser.Roles, model.ROLE_TEAM_ADMIN) { - activeAdmins = activeAdmins + 1 - } - } - - if activeAdmins <= 0 { - c.Err = model.NewLocAppError("updateRoles", "api.user.update_roles.one_admin.app_error", nil, "") - return nil - } - } - } - } user.Roles = roles @@ -1513,35 +1514,13 @@ func updateActive(c *Context, w http.ResponseWriter, r *http.Request) { user = result.Data.(*model.User) } - if !c.HasPermissionsToTeam(user.TeamId, "updateActive") { - return - } + // true when you're trying to de-activate yourself + isSelfDeactive := !active && user_id == c.Session.UserId - if !c.IsTeamAdmin() { + if !isSelfDeactive && !c.IsSystemAdmin() { c.Err = model.NewLocAppError("updateActive", "api.user.update_active.permissions.app_error", nil, "userId="+user_id) c.Err.StatusCode = http.StatusForbidden - return - } - - // make sure there is at least 1 other active admin - if !active && model.IsInRole(user.Roles, model.ROLE_TEAM_ADMIN) { - if result := <-Srv.Store.User().GetProfiles(user.TeamId); result.Err != nil { - c.Err = result.Err - return - } else { - activeAdmins := -1 - profileUsers := result.Data.(map[string]*model.User) - for _, profileUser := range profileUsers { - if profileUser.DeleteAt == 0 && model.IsInRole(profileUser.Roles, model.ROLE_TEAM_ADMIN) { - activeAdmins = activeAdmins + 1 - } - } - - if activeAdmins <= 0 { - c.Err = model.NewLocAppError("updateRoles", "api.user.update_roles.one_admin.app_error", nil, "userId="+user_id) - return - } - } + return } ruser := UpdateActive(c, user, active) @@ -1631,12 +1610,33 @@ func PermanentDeleteUser(c *Context, user *model.User) *model.AppError { return result.Err } + if result := <-Srv.Store.Team().RemoveAllMembersByUser(user.Id); result.Err != nil { + return result.Err + } + + if result := <-Srv.Store.PasswordRecovery().Delete(user.Id); result.Err != nil { + return result.Err + } + l4g.Warn(utils.T("api.user.permanent_delete_user.deleted.warn"), user.Email, user.Id) c.LogAuditWithUserId("", fmt.Sprintf("success userId=%v", user.Id)) return nil } +func PermanentDeleteAllUsers(c *Context) *model.AppError { + if result := <-Srv.Store.User().GetAll(); result.Err != nil { + return result.Err + } else { + users := result.Data.([]*model.User) + for _, user := range users { + PermanentDeleteUser(c, user) + } + } + + return nil +} + func sendPasswordReset(c *Context, w http.ResponseWriter, r *http.Request) { props := model.MapFromJson(r.Body) @@ -1646,41 +1646,28 @@ func sendPasswordReset(c *Context, w http.ResponseWriter, r *http.Request) { return } - name := props["name"] - if len(name) == 0 { - c.SetInvalidParam("sendPasswordReset", "name") - return - } - - var team *model.Team - if result := <-Srv.Store.Team().GetByName(name); result.Err != nil { - c.Err = result.Err - return - } else { - team = result.Data.(*model.Team) - } - var user *model.User - if result := <-Srv.Store.User().GetByEmail(team.Id, email); result.Err != nil { - c.Err = model.NewLocAppError("sendPasswordReset", "api.user.send_password_reset.find.app_error", nil, "email="+email+" team_id="+team.Id) + if result := <-Srv.Store.User().GetByEmail(email); result.Err != nil { + c.Err = model.NewLocAppError("sendPasswordReset", "api.user.send_password_reset.find.app_error", nil, "email="+email) return } else { user = result.Data.(*model.User) } if len(user.AuthData) != 0 { - c.Err = model.NewLocAppError("sendPasswordReset", "api.user.send_password_reset.sso.app_error", nil, "userId="+user.Id+", teamId="+team.Id) + c.Err = model.NewLocAppError("sendPasswordReset", "api.user.send_password_reset.sso.app_error", nil, "userId="+user.Id) return } - newProps := make(map[string]string) - newProps["user_id"] = user.Id - newProps["time"] = fmt.Sprintf("%v", model.GetMillis()) + recovery := &model.PasswordRecovery{} + recovery.UserId = user.Id - data := model.MapToJson(newProps) - hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.EmailSettings.PasswordResetSalt)) + if result := <-Srv.Store.PasswordRecovery().SaveOrUpdate(recovery); result.Err != nil { + c.Err = result.Err + return + } - link := fmt.Sprintf("%s/reset_password_complete?d=%s&h=%s", c.GetTeamURLFromTeam(team), url.QueryEscape(data), url.QueryEscape(hash)) + link := fmt.Sprintf("%s/reset_password_complete?code=%s", c.GetSiteURL(), url.QueryEscape(recovery.Code)) subjectPage := utils.NewHTMLTemplate("reset_subject", c.Locale) subjectPage.Props["Subject"] = c.T("api.templates.reset_subject") @@ -1706,110 +1693,89 @@ func resetPassword(c *Context, w http.ResponseWriter, r *http.Request) { props := model.MapFromJson(r.Body) newPassword := props["new_password"] - if len(newPassword) < 5 { + if len(newPassword) < model.MIN_PASSWORD_LENGTH { c.SetInvalidParam("resetPassword", "new_password") return } - name := props["name"] - if len(name) == 0 { - c.SetInvalidParam("resetPassword", "name") + code := props["code"] + if len(code) != model.PASSWORD_RECOVERY_CODE_SIZE { + c.SetInvalidParam("resetPassword", "code") return } - userId := props["user_id"] - hash := props["hash"] - timeStr := "" - - if !c.IsSystemAdmin() { - if len(hash) == 0 { - c.SetInvalidParam("resetPassword", "hash") - return - } + c.LogAudit("attempt") - data := model.MapFromJson(strings.NewReader(props["data"])) + userId := "" - userId = data["user_id"] + if result := <-Srv.Store.PasswordRecovery().GetByCode(code); result.Err != nil { + c.LogAuditWithUserId(userId, "fail - bad code") + c.Err = model.NewLocAppError("resetPassword", "api.user.reset_password.invalid_link.app_error", nil, result.Err.Error()) + return + } else { + recovery := result.Data.(*model.PasswordRecovery) - timeStr = data["time"] - if len(timeStr) == 0 { - c.SetInvalidParam("resetPassword", "data:time") + if model.GetMillis()-recovery.CreateAt < model.PASSWORD_RECOVER_EXPIRY_TIME { + userId = recovery.UserId + } else { + c.LogAuditWithUserId(userId, "fail - link expired") + c.Err = model.NewLocAppError("resetPassword", "api.user.reset_password.link_expired.app_error", nil, "") return } + + go func() { + if result := <-Srv.Store.PasswordRecovery().Delete(userId); result.Err != nil { + l4g.Error("%v", result.Err) + } + }() } - if len(userId) != 26 { - c.SetInvalidParam("resetPassword", "user_id") + if err := ResetPassword(c, userId, newPassword); err != nil { + c.Err = err return } - c.LogAuditWithUserId(userId, "attempt") + c.LogAuditWithUserId(userId, "success") - var team *model.Team - if result := <-Srv.Store.Team().GetByName(name); result.Err != nil { - c.Err = result.Err - return - } else { - team = result.Data.(*model.Team) - } + rdata := map[string]string{} + rdata["status"] = "ok" + w.Write([]byte(model.MapToJson(rdata))) +} +func ResetPassword(c *Context, userId, newPassword string) *model.AppError { var user *model.User if result := <-Srv.Store.User().Get(userId); result.Err != nil { - c.Err = result.Err - return + return result.Err } else { user = result.Data.(*model.User) } if len(user.AuthData) != 0 { - c.Err = model.NewLocAppError("resetPassword", "api.user.reset_password.sso.app_error", nil, "userId="+user.Id+", teamId="+team.Id) - return - } - - if user.TeamId != team.Id { - c.Err = model.NewLocAppError("resetPassword", "api.user.reset_password.wrong_team.app_error", nil, "userId="+user.Id+", teamId="+team.Id) - c.Err.StatusCode = http.StatusForbidden - return - } - - if !c.IsSystemAdmin() { - if !model.ComparePassword(hash, fmt.Sprintf("%v:%v", props["data"], utils.Cfg.EmailSettings.PasswordResetSalt)) { - c.Err = model.NewLocAppError("resetPassword", "api.user.reset_password.invalid_link.app_error", nil, "") - return - } + return model.NewLocAppError("ResetPassword", "api.user.reset_password.sso.app_error", nil, "userId="+user.Id) - t, err := strconv.ParseInt(timeStr, 10, 64) - if err != nil || model.GetMillis()-t > 1000*60*60 { // one hour - c.Err = model.NewLocAppError("resetPassword", "api.user.reset_password.link_expired.app_error", nil, "") - return - } } if result := <-Srv.Store.User().UpdatePassword(userId, model.HashPassword(newPassword)); result.Err != nil { - c.Err = result.Err - return - } else { - c.LogAuditWithUserId(userId, "success") + return result.Err } - sendPasswordChangeEmailAndForget(c, user.Email, team.DisplayName, c.GetTeamURLFromTeam(team), c.GetSiteURL(), c.T("api.user.reset_password.method")) + sendPasswordChangeEmailAndForget(c, user.Email, c.GetSiteURL(), c.T("api.user.reset_password.method")) - props["new_password"] = "" - w.Write([]byte(model.MapToJson(props))) + return nil } -func sendPasswordChangeEmailAndForget(c *Context, email, teamDisplayName, teamURL, siteURL, method string) { +func sendPasswordChangeEmailAndForget(c *Context, email, siteURL, method string) { go func() { subjectPage := utils.NewHTMLTemplate("password_change_subject", c.Locale) subjectPage.Props["Subject"] = c.T("api.templates.password_change_subject", - map[string]interface{}{"TeamDisplayName": teamDisplayName, "SiteName": utils.ClientCfg["SiteName"]}) + map[string]interface{}{"TeamDisplayName": utils.Cfg.TeamSettings.SiteName, "SiteName": utils.Cfg.TeamSettings.SiteName}) bodyPage := utils.NewHTMLTemplate("password_change_body", c.Locale) bodyPage.Props["SiteURL"] = siteURL bodyPage.Props["Title"] = c.T("api.templates.password_change_body.title") bodyPage.Html["Info"] = template.HTML(c.T("api.templates.password_change_body.info", - map[string]interface{}{"TeamDisplayName": teamDisplayName, "TeamURL": teamURL, "Method": method})) + map[string]interface{}{"TeamDisplayName": utils.Cfg.TeamSettings.SiteName, "TeamURL": siteURL, "Method": method})) if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil { l4g.Error(utils.T("api.user.send_password_change_email_and_forget.error"), err) @@ -1818,19 +1784,19 @@ func sendPasswordChangeEmailAndForget(c *Context, email, teamDisplayName, teamUR }() } -func sendEmailChangeEmailAndForget(c *Context, oldEmail, newEmail, teamDisplayName, teamURL, siteURL string) { +func sendEmailChangeEmailAndForget(c *Context, oldEmail, newEmail, siteURL string) { go func() { subjectPage := utils.NewHTMLTemplate("email_change_subject", c.Locale) subjectPage.Props["Subject"] = c.T("api.templates.email_change_subject", - map[string]interface{}{"TeamDisplayName": teamDisplayName}) + map[string]interface{}{"TeamDisplayName": utils.Cfg.TeamSettings.SiteName}) subjectPage.Props["SiteName"] = utils.Cfg.TeamSettings.SiteName bodyPage := utils.NewHTMLTemplate("email_change_body", c.Locale) bodyPage.Props["SiteURL"] = siteURL bodyPage.Props["Title"] = c.T("api.templates.email_change_body.title") bodyPage.Html["Info"] = template.HTML(c.T("api.templates.email_change_body.info", - map[string]interface{}{"TeamDisplayName": teamDisplayName, "NewEmail": newEmail})) + map[string]interface{}{"TeamDisplayName": utils.Cfg.TeamSettings.SiteName, "NewEmail": newEmail})) if err := utils.SendMail(oldEmail, subjectPage.Render(), bodyPage.Render()); err != nil { l4g.Error(utils.T("api.user.send_email_change_email_and_forget.error"), err) @@ -1839,21 +1805,21 @@ func sendEmailChangeEmailAndForget(c *Context, oldEmail, newEmail, teamDisplayNa }() } -func SendEmailChangeVerifyEmailAndForget(c *Context, userId, newUserEmail, teamName, teamDisplayName, siteURL, teamURL string) { +func SendEmailChangeVerifyEmailAndForget(c *Context, userId, newUserEmail, siteURL string) { go func() { - link := fmt.Sprintf("%s/do_verify_email?uid=%s&hid=%s&teamname=%s&email=%s", siteURL, userId, model.HashPassword(userId), teamName, newUserEmail) + link := fmt.Sprintf("%s/do_verify_email?uid=%s&hid=%s&email=%s", siteURL, userId, model.HashPassword(userId), newUserEmail) subjectPage := utils.NewHTMLTemplate("email_change_verify_subject", c.Locale) subjectPage.Props["Subject"] = c.T("api.templates.email_change_verify_subject", - map[string]interface{}{"TeamDisplayName": teamDisplayName}) + map[string]interface{}{"TeamDisplayName": utils.Cfg.TeamSettings.SiteName}) subjectPage.Props["SiteName"] = utils.Cfg.TeamSettings.SiteName bodyPage := utils.NewHTMLTemplate("email_change_verify_body", c.Locale) bodyPage.Props["SiteURL"] = siteURL bodyPage.Props["Title"] = c.T("api.templates.email_change_verify_body.title") bodyPage.Props["Info"] = c.T("api.templates.email_change_verify_body.info", - map[string]interface{}{"TeamDisplayName": teamDisplayName}) + map[string]interface{}{"TeamDisplayName": utils.Cfg.TeamSettings.SiteName}) bodyPage.Props["VerifyUrl"] = link bodyPage.Props["VerifyButton"] = c.T("api.templates.email_change_verify_body.button") @@ -1929,7 +1895,7 @@ func getStatuses(c *Context, w http.ResponseWriter, r *http.Request) { return } - if result := <-Srv.Store.User().GetProfiles(c.Session.TeamId); result.Err != nil { + if result := <-Srv.Store.User().GetProfileByIds(userIds); result.Err != nil { c.Err = result.Err return } else { @@ -1937,17 +1903,6 @@ func getStatuses(c *Context, w http.ResponseWriter, r *http.Request) { statuses := map[string]string{} for _, profile := range profiles { - found := false - for _, uid := range userIds { - if uid == profile.Id { - found = true - } - } - - if !found { - continue - } - if profile.IsOffline() { statuses[profile.Id] = model.USER_OFFLINE } else if profile.IsAway() { @@ -1957,131 +1912,18 @@ func getStatuses(c *Context, w http.ResponseWriter, r *http.Request) { } } - //w.Header().Set("Cache-Control", "max-age=9, public") // 2 mins w.Write([]byte(model.MapToJson(statuses))) return } } -func GetAuthorizationCode(c *Context, service, teamName string, props map[string]string, loginHint string) (string, *model.AppError) { - - sso := utils.Cfg.GetSSOService(service) - if sso != nil && !sso.Enable { - return "", model.NewLocAppError("GetAuthorizationCode", "api.user.get_authorization_code.unsupported.app_error", nil, "service="+service) - } - - clientId := sso.Id - endpoint := sso.AuthEndpoint - scope := sso.Scope - - props["hash"] = model.HashPassword(clientId) - props["team"] = teamName - state := b64.StdEncoding.EncodeToString([]byte(model.MapToJson(props))) - - redirectUri := c.GetSiteURL() + "/signup/" + service + "/complete" - - authUrl := endpoint + "?response_type=code&client_id=" + clientId + "&redirect_uri=" + url.QueryEscape(redirectUri) + "&state=" + url.QueryEscape(state) - - if len(scope) > 0 { - authUrl += "&scope=" + utils.UrlEncode(scope) - } - - if len(loginHint) > 0 { - authUrl += "&login_hint=" + utils.UrlEncode(loginHint) - } - - return authUrl, nil -} - -func AuthorizeOAuthUser(service, code, state, redirectUri string) (io.ReadCloser, *model.Team, map[string]string, *model.AppError) { - sso := utils.Cfg.GetSSOService(service) - if sso == nil || !sso.Enable { - return nil, nil, nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.unsupported.app_error", nil, "service="+service) - } - - stateStr := "" - if b, err := b64.StdEncoding.DecodeString(state); err != nil { - return nil, nil, nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.invalid_state.app_error", nil, err.Error()) - } else { - stateStr = string(b) - } - - stateProps := model.MapFromJson(strings.NewReader(stateStr)) - - if !model.ComparePassword(stateProps["hash"], sso.Id) { - return nil, nil, nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.invalid_state.app_error", nil, "") - } - - ok := true - teamName := "" - if teamName, ok = stateProps["team"]; !ok { - return nil, nil, nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.invalid_state_team.app_error", nil, "") - } - - tchan := Srv.Store.Team().GetByName(teamName) - - p := url.Values{} - p.Set("client_id", sso.Id) - p.Set("client_secret", sso.Secret) - p.Set("code", code) - p.Set("grant_type", model.ACCESS_TOKEN_GRANT_TYPE) - p.Set("redirect_uri", redirectUri) - - tr := &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: *utils.Cfg.ServiceSettings.EnableInsecureOutgoingConnections}, - } - client := &http.Client{Transport: tr} - req, _ := http.NewRequest("POST", sso.TokenEndpoint, strings.NewReader(p.Encode())) - - req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - req.Header.Set("Accept", "application/json") - - var ar *model.AccessResponse - if resp, err := client.Do(req); err != nil { - return nil, nil, nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.token_failed.app_error", nil, err.Error()) - } else { - ar = model.AccessResponseFromJson(resp.Body) - if ar == nil { - return nil, nil, nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.bad_response.app_error", nil, "") - } - } - - if strings.ToLower(ar.TokenType) != model.ACCESS_TOKEN_TYPE { - return nil, nil, nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.bad_token.app_error", nil, "token_type="+ar.TokenType) - } - - if len(ar.AccessToken) == 0 { - return nil, nil, nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.missing.app_error", nil, "") - } - - p = url.Values{} - p.Set("access_token", ar.AccessToken) - req, _ = http.NewRequest("GET", sso.UserApiEndpoint, strings.NewReader("")) - - req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - req.Header.Set("Accept", "application/json") - req.Header.Set("Authorization", "Bearer "+ar.AccessToken) - - if resp, err := client.Do(req); err != nil { - return nil, nil, nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.service.app_error", - map[string]interface{}{"Service": service}, err.Error()) - } else { - if result := <-tchan; result.Err != nil { - return nil, nil, nil, result.Err - } else { - return resp.Body, result.Data.(*model.Team), stateProps, nil - } - } - -} - -func IsUsernameTaken(name string, teamId string) bool { +func IsUsernameTaken(name string) bool { if !model.IsValidUsername(name) { return false } - if result := <-Srv.Store.User().GetByUsername(teamId, name); result.Err != nil { + if result := <-Srv.Store.User().GetByUsername(name); result.Err != nil { return false } else { return true @@ -2099,12 +1941,6 @@ func emailToOAuth(c *Context, w http.ResponseWriter, r *http.Request) { return } - teamName := props["team_name"] - if len(teamName) == 0 { - c.SetInvalidParam("emailToOAuth", "team_name") - return - } - service := props["service"] if len(service) == 0 { c.SetInvalidParam("emailToOAuth", "service") @@ -2119,17 +1955,8 @@ func emailToOAuth(c *Context, w http.ResponseWriter, r *http.Request) { c.LogAudit("attempt") - var team *model.Team - if result := <-Srv.Store.Team().GetByName(teamName); result.Err != nil { - c.LogAudit("fail - couldn't get team") - c.Err = result.Err - return - } else { - team = result.Data.(*model.Team) - } - var user *model.User - if result := <-Srv.Store.User().GetByEmail(team.Id, email); result.Err != nil { + if result := <-Srv.Store.User().GetByEmail(email); result.Err != nil { c.LogAudit("fail - couldn't get user") c.Err = result.Err return @@ -2137,8 +1964,9 @@ func emailToOAuth(c *Context, w http.ResponseWriter, r *http.Request) { user = result.Data.(*model.User) } - if !checkUserLoginAttempts(c, user) || !checkUserPassword(c, user, password) { - c.LogAuditWithUserId(user.Id, "fail - invalid password") + if err := checkPasswordAndAllCriteria(user, password, ""); err != nil { + c.LogAuditWithUserId(user.Id, "failed - bad authentication") + c.Err = err return } @@ -2147,7 +1975,7 @@ func emailToOAuth(c *Context, w http.ResponseWriter, r *http.Request) { stateProps["email"] = email m := map[string]string{} - if authUrl, err := GetAuthorizationCode(c, service, teamName, stateProps, ""); err != nil { + if authUrl, err := GetAuthorizationCode(c, service, stateProps, ""); err != nil { c.LogAuditWithUserId(user.Id, "fail - oauth issue") c.Err = err return @@ -2159,52 +1987,6 @@ func emailToOAuth(c *Context, w http.ResponseWriter, r *http.Request) { w.Write([]byte(model.MapToJson(m))) } -func CompleteSwitchWithOAuth(c *Context, w http.ResponseWriter, r *http.Request, service string, userData io.ReadCloser, team *model.Team, email string) { - authData := "" - ssoEmail := "" - provider := einterfaces.GetOauthProvider(service) - if provider == nil { - c.Err = model.NewLocAppError("CompleteClaimWithOAuth", "api.user.complete_switch_with_oauth.unavailable.app_error", - map[string]interface{}{"Service": service}, "") - return - } else { - ssoUser := provider.GetUserFromJson(userData) - authData = ssoUser.AuthData - ssoEmail = ssoUser.Email - } - - if len(authData) == 0 { - c.Err = model.NewLocAppError("CompleteClaimWithOAuth", "api.user.complete_switch_with_oauth.parse.app_error", - map[string]interface{}{"Service": service}, "") - return - } - - if len(email) == 0 { - c.Err = model.NewLocAppError("CompleteClaimWithOAuth", "api.user.complete_switch_with_oauth.blank_email.app_error", nil, "") - return - } - - var user *model.User - if result := <-Srv.Store.User().GetByEmail(team.Id, email); result.Err != nil { - c.Err = result.Err - return - } else { - user = result.Data.(*model.User) - } - - RevokeAllSession(c, user.Id) - if c.Err != nil { - return - } - - if result := <-Srv.Store.User().UpdateAuthData(user.Id, service, authData, ssoEmail); result.Err != nil { - c.Err = result.Err - return - } - - sendSignInChangeEmailAndForget(c, user.Email, team.DisplayName, c.GetSiteURL()+"/"+team.Name, c.GetSiteURL(), strings.Title(service)+" SSO") -} - func oauthToEmail(c *Context, w http.ResponseWriter, r *http.Request) { props := model.MapFromJson(r.Body) @@ -2214,12 +1996,6 @@ func oauthToEmail(c *Context, w http.ResponseWriter, r *http.Request) { return } - teamName := props["team_name"] - if len(teamName) == 0 { - c.SetInvalidParam("oauthToEmail", "team_name") - return - } - email := props["email"] if len(email) == 0 { c.SetInvalidParam("oauthToEmail", "email") @@ -2228,17 +2004,8 @@ func oauthToEmail(c *Context, w http.ResponseWriter, r *http.Request) { c.LogAudit("attempt") - var team *model.Team - if result := <-Srv.Store.Team().GetByName(teamName); result.Err != nil { - c.LogAudit("fail - couldn't get team") - c.Err = result.Err - return - } else { - team = result.Data.(*model.Team) - } - var user *model.User - if result := <-Srv.Store.User().GetByEmail(team.Id, email); result.Err != nil { + if result := <-Srv.Store.User().GetByEmail(email); result.Err != nil { c.LogAudit("fail - couldn't get user") c.Err = result.Err return @@ -2259,7 +2026,7 @@ func oauthToEmail(c *Context, w http.ResponseWriter, r *http.Request) { return } - sendSignInChangeEmailAndForget(c, user.Email, team.DisplayName, c.GetSiteURL()+"/"+team.Name, c.GetSiteURL(), c.T("api.templates.signin_change_email.body.method_email")) + sendSignInChangeEmailAndForget(c, user.Email, c.GetSiteURL(), c.T("api.templates.signin_change_email.body.method_email")) RevokeAllSession(c, c.Session.UserId) if c.Err != nil { @@ -2267,7 +2034,7 @@ func oauthToEmail(c *Context, w http.ResponseWriter, r *http.Request) { } m := map[string]string{} - m["follow_link"] = c.GetTeamURL() + "/login?extra=signin_change" + m["follow_link"] = "/login?extra=signin_change" c.LogAudit("success") w.Write([]byte(model.MapToJson(m))) @@ -2288,12 +2055,6 @@ func emailToLdap(c *Context, w http.ResponseWriter, r *http.Request) { return } - teamName := props["team_name"] - if len(teamName) == 0 { - c.SetInvalidParam("emailToLdap", "team_name") - return - } - ldapId := props["ldap_id"] if len(ldapId) == 0 { c.SetInvalidParam("emailToLdap", "ldap_id") @@ -2308,17 +2069,8 @@ func emailToLdap(c *Context, w http.ResponseWriter, r *http.Request) { c.LogAudit("attempt") - var team *model.Team - if result := <-Srv.Store.Team().GetByName(teamName); result.Err != nil { - c.LogAudit("fail - couldn't get team") - c.Err = result.Err - return - } else { - team = result.Data.(*model.Team) - } - var user *model.User - if result := <-Srv.Store.User().GetByEmail(team.Id, email); result.Err != nil { + if result := <-Srv.Store.User().GetByEmail(email); result.Err != nil { c.LogAudit("fail - couldn't get user") c.Err = result.Err return @@ -2326,8 +2078,9 @@ func emailToLdap(c *Context, w http.ResponseWriter, r *http.Request) { user = result.Data.(*model.User) } - if !checkUserLoginAttempts(c, user) || !checkUserPassword(c, user, emailPassword) { - c.LogAuditWithUserId(user.Id, "fail - invalid email password") + if err := checkPasswordAndAllCriteria(user, emailPassword, ""); err != nil { + c.LogAuditWithUserId(user.Id, "failed - bad authentication") + c.Err = err return } @@ -2343,16 +2096,16 @@ func emailToLdap(c *Context, w http.ResponseWriter, r *http.Request) { return } - if err := ldapInterface.SwitchToEmail(user.Id, ldapId, ldapPassword); err != nil { + if err := ldapInterface.SwitchToLdap(user.Id, ldapId, ldapPassword); err != nil { c.LogAuditWithUserId(user.Id, "fail - ldap switch failed") c.Err = err return } - sendSignInChangeEmailAndForget(c, user.Email, team.DisplayName, c.GetSiteURL()+"/"+team.Name, c.GetSiteURL(), "LDAP") + sendSignInChangeEmailAndForget(c, user.Email, c.GetSiteURL(), "LDAP") m := map[string]string{} - m["follow_link"] = c.GetTeamURL() + "/login?extra=signin_change" + m["follow_link"] = "/login?extra=signin_change" c.LogAudit("success") w.Write([]byte(model.MapToJson(m))) @@ -2373,12 +2126,6 @@ func ldapToEmail(c *Context, w http.ResponseWriter, r *http.Request) { return } - teamName := props["team_name"] - if len(teamName) == 0 { - c.SetInvalidParam("ldapToEmail", "team_name") - return - } - ldapPassword := props["ldap_password"] if len(ldapPassword) == 0 { c.SetInvalidParam("ldapToEmail", "ldap_password") @@ -2387,17 +2134,8 @@ func ldapToEmail(c *Context, w http.ResponseWriter, r *http.Request) { c.LogAudit("attempt") - var team *model.Team - if result := <-Srv.Store.Team().GetByName(teamName); result.Err != nil { - c.LogAudit("fail - couldn't get team") - c.Err = result.Err - return - } else { - team = result.Data.(*model.Team) - } - var user *model.User - if result := <-Srv.Store.User().GetByEmail(team.Id, email); result.Err != nil { + if result := <-Srv.Store.User().GetByEmail(email); result.Err != nil { c.LogAudit("fail - couldn't get user") c.Err = result.Err return @@ -2434,27 +2172,27 @@ func ldapToEmail(c *Context, w http.ResponseWriter, r *http.Request) { return } - sendSignInChangeEmailAndForget(c, user.Email, team.DisplayName, c.GetSiteURL()+"/"+team.Name, c.GetSiteURL(), c.T("api.templates.signin_change_email.body.method_email")) + sendSignInChangeEmailAndForget(c, user.Email, c.GetSiteURL(), c.T("api.templates.signin_change_email.body.method_email")) m := map[string]string{} - m["follow_link"] = c.GetTeamURL() + "/login?extra=signin_change" + m["follow_link"] = "/login?extra=signin_change" c.LogAudit("success") w.Write([]byte(model.MapToJson(m))) } -func sendSignInChangeEmailAndForget(c *Context, email, teamDisplayName, teamURL, siteURL, method string) { +func sendSignInChangeEmailAndForget(c *Context, email, siteURL, method string) { go func() { subjectPage := utils.NewHTMLTemplate("signin_change_subject", c.Locale) subjectPage.Props["Subject"] = c.T("api.templates.singin_change_email.subject", - map[string]interface{}{"TeamDisplayName": teamDisplayName, "SiteName": utils.ClientCfg["SiteName"]}) + map[string]interface{}{"SiteName": utils.ClientCfg["SiteName"]}) bodyPage := utils.NewHTMLTemplate("signin_change_body", c.Locale) bodyPage.Props["SiteURL"] = siteURL bodyPage.Props["Title"] = c.T("api.templates.signin_change_email.body.title") bodyPage.Html["Info"] = template.HTML(c.T("api.templates.singin_change_email.body.info", - map[string]interface{}{"TeamDisplayName": teamDisplayName, "TeamURL": teamURL, "Method": method})) + map[string]interface{}{"SiteName": utils.ClientCfg["SiteName"], "Method": method})) if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil { l4g.Error(utils.T("api.user.send_sign_in_change_email_and_forget.error"), err) @@ -2488,49 +2226,34 @@ func verifyEmail(c *Context, w http.ResponseWriter, r *http.Request) { } c.Err = model.NewLocAppError("verifyEmail", "api.user.verify_email.bad_link.app_error", nil, "") - c.Err.StatusCode = http.StatusForbidden + c.Err.StatusCode = http.StatusBadRequest } func resendVerification(c *Context, w http.ResponseWriter, r *http.Request) { props := model.MapFromJson(r.Body) - teamName := props["team_name"] - if len(teamName) == 0 { - c.SetInvalidParam("resendVerification", "team_name") - return - } - email := props["email"] if len(email) == 0 { c.SetInvalidParam("resendVerification", "email") return } - var team *model.Team - if result := <-Srv.Store.Team().GetByName(teamName); result.Err != nil { - c.Err = result.Err - return - } else { - team = result.Data.(*model.Team) - } - - if result := <-Srv.Store.User().GetByEmail(team.Id, email); result.Err != nil { + if result := <-Srv.Store.User().GetByEmail(email); result.Err != nil { c.Err = result.Err return } else { user := result.Data.(*model.User) if user.LastActivityAt > 0 { - SendEmailChangeVerifyEmailAndForget(c, user.Id, user.Email, team.Name, team.DisplayName, c.GetSiteURL(), c.GetTeamURLFromTeam(team)) + SendEmailChangeVerifyEmailAndForget(c, user.Id, user.Email, c.GetSiteURL()) } else { - SendVerifyEmailAndForget(c, user.Id, user.Email, team.Name, team.DisplayName, c.GetSiteURL(), c.GetTeamURLFromTeam(team)) + SendVerifyEmailAndForget(c, user.Id, user.Email, c.GetSiteURL()) } } } func generateMfaQrCode(c *Context, w http.ResponseWriter, r *http.Request) { uchan := Srv.Store.User().Get(c.Session.UserId) - tchan := Srv.Store.Team().Get(c.Session.TeamId) var user *model.User if result := <-uchan; result.Err != nil { @@ -2540,14 +2263,6 @@ func generateMfaQrCode(c *Context, w http.ResponseWriter, r *http.Request) { user = result.Data.(*model.User) } - var team *model.Team - if result := <-tchan; result.Err != nil { - c.Err = result.Err - return - } else { - team = result.Data.(*model.Team) - } - mfaInterface := einterfaces.GetMfaInterface() if mfaInterface == nil { c.Err = model.NewLocAppError("generateMfaQrCode", "api.user.generate_mfa_qr.not_available.app_error", nil, "") @@ -2555,7 +2270,7 @@ func generateMfaQrCode(c *Context, w http.ResponseWriter, r *http.Request) { return } - img, err := mfaInterface.GenerateQrCode(team, user) + img, err := mfaInterface.GenerateQrCode(user) if err != nil { c.Err = err return @@ -2583,28 +2298,13 @@ func updateMfa(c *Context, w http.ResponseWriter, r *http.Request) { } } - mfaInterface := einterfaces.GetMfaInterface() - if mfaInterface == nil { - c.Err = model.NewLocAppError("generateMfaQrCode", "api.user.update_mfa.not_available.app_error", nil, "") - c.Err.StatusCode = http.StatusNotImplemented - return - } - if activate { - var user *model.User - if result := <-Srv.Store.User().Get(c.Session.UserId); result.Err != nil { - c.Err = result.Err - return - } else { - user = result.Data.(*model.User) - } - - if err := mfaInterface.Activate(user, token); err != nil { + if err := ActivateMfa(c.Session.UserId, token); err != nil { c.Err = err return } } else { - if err := mfaInterface.Deactivate(c.Session.UserId); err != nil { + if err := DeactivateMfa(c.Session.UserId); err != nil { c.Err = err return } @@ -2615,6 +2315,43 @@ func updateMfa(c *Context, w http.ResponseWriter, r *http.Request) { w.Write([]byte(model.MapToJson(rdata))) } +func ActivateMfa(userId, token string) *model.AppError { + mfaInterface := einterfaces.GetMfaInterface() + if mfaInterface == nil { + err := model.NewLocAppError("ActivateMfa", "api.user.update_mfa.not_available.app_error", nil, "") + err.StatusCode = http.StatusNotImplemented + return err + } + + var user *model.User + if result := <-Srv.Store.User().Get(userId); result.Err != nil { + return result.Err + } else { + user = result.Data.(*model.User) + } + + if err := mfaInterface.Activate(user, token); err != nil { + return err + } + + return nil +} + +func DeactivateMfa(userId string) *model.AppError { + mfaInterface := einterfaces.GetMfaInterface() + if mfaInterface == nil { + err := model.NewLocAppError("DeactivateMfa", "api.user.update_mfa.not_available.app_error", nil, "") + err.StatusCode = http.StatusNotImplemented + return err + } + + if err := mfaInterface.Deactivate(userId); err != nil { + return err + } + + return nil +} + func checkMfa(c *Context, w http.ResponseWriter, r *http.Request) { if !utils.IsLicensed || !*utils.License.Features.MFA || !*utils.Cfg.ServiceSettings.EnableMultifactorAuthentication { rdata := map[string]string{} @@ -2633,33 +2370,19 @@ func checkMfa(c *Context, w http.ResponseWriter, r *http.Request) { return } - teamName := props["team_name"] - if len(teamName) == 0 { - c.SetInvalidParam("checkMfa", "team_name") - return - } - loginId := props["login_id"] if len(loginId) == 0 { c.SetInvalidParam("checkMfa", "login_id") return } - var team *model.Team - if result := <-Srv.Store.Team().GetByName(teamName); result.Err != nil { - c.Err = result.Err - return - } else { - team = result.Data.(*model.Team) - } - var uchan store.StoreChannel if method == model.USER_AUTH_SERVICE_EMAIL { - uchan = Srv.Store.User().GetByEmail(team.Id, loginId) + uchan = Srv.Store.User().GetByEmail(loginId) } else if method == model.USER_AUTH_SERVICE_USERNAME { - uchan = Srv.Store.User().GetByUsername(team.Id, loginId) + uchan = Srv.Store.User().GetByUsername(loginId) } else if method == model.USER_AUTH_SERVICE_LDAP { - uchan = Srv.Store.User().GetByAuth(team.Id, loginId, model.USER_AUTH_SERVICE_LDAP) + uchan = Srv.Store.User().GetByAuth(loginId, model.USER_AUTH_SERVICE_LDAP) } rdata := map[string]string{} diff --git a/api/user_test.go b/api/user_test.go index 33f3fdad4..3c744120c 100644 --- a/api/user_test.go +++ b/api/user_test.go @@ -6,11 +6,6 @@ package api import ( "bytes" "fmt" - "github.com/goamz/goamz/aws" - "github.com/goamz/goamz/s3" - "github.com/mattermost/platform/model" - "github.com/mattermost/platform/store" - "github.com/mattermost/platform/utils" "image" "image/color" "io" @@ -20,21 +15,31 @@ import ( "strings" "testing" "time" + + "github.com/goamz/goamz/aws" + "github.com/goamz/goamz/s3" + + "github.com/mattermost/platform/model" + "github.com/mattermost/platform/store" + "github.com/mattermost/platform/utils" ) func TestCreateUser(t *testing.T) { - Setup() + th := Setup() + Client := th.CreateClient() team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} rteam, _ := Client.CreateTeam(&team) - user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "hello"} + user := model.User{Email: strings.ToLower("success+"+model.NewId()) + "@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "hello", Username: "n" + model.NewId()} ruser, err := Client.CreateUser(&user, "") if err != nil { t.Fatal(err) } + LinkUserToTeam(ruser.Data.(*model.User), rteam.Data.(*model.Team)) + if ruser.Data.(*model.User).Nickname != user.Nickname { t.Fatal("nickname didn't match") } @@ -48,13 +53,15 @@ func TestCreateUser(t *testing.T) { } ruser.Data.(*model.User).Id = "" + ruser.Data.(*model.User).Username = "n" + model.NewId() if _, err := Client.CreateUser(ruser.Data.(*model.User), ""); err != nil { if err.Message != "An account with that email already exists." { t.Fatal(err) } } - ruser.Data.(*model.User).Email = "test2@nowhere.com" + ruser.Data.(*model.User).Email = "success+" + model.NewId() + "@simulator.amazonses.com" + ruser.Data.(*model.User).Username = user.Username if _, err := Client.CreateUser(ruser.Data.(*model.User), ""); err != nil { if err.Message != "An account with that username already exists." { t.Fatal(err) @@ -73,34 +80,16 @@ func TestCreateUser(t *testing.T) { } } -func TestCreateUserAllowedDomains(t *testing.T) { - Setup() - - team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_INVITE, AllowedDomains: "spinpunch.com, @nowh.com,@hello.com"} - rteam, _ := Client.CreateTeam(&team) - - user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "hello"} - - _, err := Client.CreateUser(&user, "") - if err == nil { - t.Fatal("should have failed") - } - - user.Email = "test@nowh.com" - _, err = Client.CreateUser(&user, "") - if err != nil { - t.Fatal(err) - } -} - func TestLogin(t *testing.T) { - Setup() + th := Setup() + Client := th.CreateClient() team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} rteam, _ := Client.CreateTeam(&team) - user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Username: "corey", Password: "pwd"} + user := model.User{Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Username: "corey" + model.NewId(), Password: "pwd"} ruser, _ := Client.CreateUser(&user, "") + LinkUserToTeam(ruser.Data.(*model.User), rteam.Data.(*model.Team)) store.Must(Srv.Store.User().VerifyEmail(ruser.Data.(*model.User).Id)) if result, err := Client.LoginById(ruser.Data.(*model.User).Id, user.Password); err != nil { @@ -155,7 +144,7 @@ func TestLogin(t *testing.T) { team2 := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_INVITE} rteam2 := Client.Must(Client.CreateTeam(&team2)) - user2 := model.User{TeamId: rteam2.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} + user2 := model.User{Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} if _, err := Client.CreateUserFromSignup(&user2, "junk", "1231312"); err == nil { t.Fatal("Should have errored, signed up without hashed email") @@ -179,14 +168,11 @@ func TestLogin(t *testing.T) { } func TestLoginWithDeviceId(t *testing.T) { - Setup() - - team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - rteam, _ := Client.CreateTeam(&team) - - user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - ruser := Client.Must(Client.CreateUser(&user, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(ruser.Id)) + th := Setup().InitBasic() + Client := th.BasicClient + team := th.BasicTeam + user := th.BasicUser + Client.Must(Client.Logout()) deviceId := model.NewId() if result, err := Client.LoginByEmailWithDevice(team.Name, user.Email, user.Password, deviceId); err != nil { @@ -210,20 +196,17 @@ func TestLoginWithDeviceId(t *testing.T) { } func TestSessions(t *testing.T) { - Setup() - - team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - rteam, _ := Client.CreateTeam(&team) - - user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - ruser := Client.Must(Client.CreateUser(&user, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(ruser.Id)) + th := Setup().InitBasic() + Client := th.BasicClient + team := th.BasicTeam + user := th.BasicUser + Client.Must(Client.Logout()) deviceId := model.NewId() Client.LoginByEmailWithDevice(team.Name, user.Email, user.Password, deviceId) Client.LoginByEmail(team.Name, user.Email, user.Password) - r1, err := Client.GetSessions(ruser.Id) + r1, err := Client.GetSessions(user.Id) if err != nil { t.Fatal(err) } @@ -249,7 +232,7 @@ func TestSessions(t *testing.T) { t.Fatal(err) } - r2, err := Client.GetSessions(ruser.Id) + r2, err := Client.GetSessions(user.Id) if err != nil { t.Fatal(err) } @@ -262,24 +245,28 @@ func TestSessions(t *testing.T) { } func TestGetUser(t *testing.T) { - Setup() + th := Setup() + Client := th.CreateClient() team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} rteam, _ := Client.CreateTeam(&team) - user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} + user := model.User{Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} ruser, _ := Client.CreateUser(&user, "") + LinkUserToTeam(ruser.Data.(*model.User), rteam.Data.(*model.Team)) store.Must(Srv.Store.User().VerifyEmail(ruser.Data.(*model.User).Id)) - user2 := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} + user2 := model.User{Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} ruser2, _ := Client.CreateUser(&user2, "") + LinkUserToTeam(ruser2.Data.(*model.User), rteam.Data.(*model.Team)) store.Must(Srv.Store.User().VerifyEmail(ruser2.Data.(*model.User).Id)) team2 := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} rteam2, _ := Client.CreateTeam(&team2) - user3 := model.User{TeamId: rteam2.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} + user3 := model.User{Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} ruser3, _ := Client.CreateUser(&user3, "") + LinkUserToTeam(ruser3.Data.(*model.User), rteam2.Data.(*model.Team)) store.Must(Srv.Store.User().VerifyEmail(ruser3.Data.(*model.User).Id)) Client.LoginByEmail(team.Name, user.Email, user.Password) @@ -353,14 +340,36 @@ func TestGetUser(t *testing.T) { } } +func TestGetDirectProfiles(t *testing.T) { + th := Setup().InitBasic() + + th.BasicClient.Must(th.BasicClient.CreateDirectChannel(th.BasicUser2.Id)) + + if result, err := th.BasicClient.GetDirectProfiles(""); err != nil { + t.Fatal(err) + } else { + users := result.Data.(map[string]*model.User) + + if len(users) != 1 { + t.Fatal("map was wrong length") + } + + if users[th.BasicUser2.Id] == nil { + t.Fatal("missing expected user") + } + } +} + func TestGetAudits(t *testing.T) { - Setup() + th := Setup() + Client := th.CreateClient() team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} rteam, _ := Client.CreateTeam(&team) - user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} + user := model.User{Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} ruser, _ := Client.CreateUser(&user, "") + LinkUserToTeam(ruser.Data.(*model.User), rteam.Data.(*model.Team)) store.Must(Srv.Store.User().VerifyEmail(ruser.Data.(*model.User).Id)) time.Sleep(100 * time.Millisecond) @@ -390,7 +399,8 @@ func TestGetAudits(t *testing.T) { } func TestUserCreateImage(t *testing.T) { - Setup() + th := Setup() + Client := th.CreateClient() b, err := createProfileImage("Corey Hulen", "eo1zkdr96pdj98pjmq8zy35wba") if err != nil { @@ -412,8 +422,9 @@ func TestUserCreateImage(t *testing.T) { team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} + user := &model.User{Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) + LinkUserToTeam(user, team) store.Must(Srv.Store.User().VerifyEmail(user.Id)) Client.LoginByEmail(team.Name, user.Email, "pwd") @@ -428,26 +439,27 @@ func TestUserCreateImage(t *testing.T) { s := s3.New(auth, aws.Regions[utils.Cfg.FileSettings.AmazonS3Region]) bucket := s.Bucket(utils.Cfg.FileSettings.AmazonS3Bucket) - if err := bucket.Del("teams/" + user.TeamId + "/users/" + user.Id + "/profile.png"); err != nil { + if err := bucket.Del("/users/" + user.Id + "/profile.png"); err != nil { t.Fatal(err) } } else { - path := utils.Cfg.FileSettings.Directory + "teams/" + user.TeamId + "/users/" + user.Id + "/profile.png" + path := utils.Cfg.FileSettings.Directory + "/users/" + user.Id + "/profile.png" if err := os.Remove(path); err != nil { t.Fatal("Couldn't remove file at " + path) } } - } func TestUserUploadProfileImage(t *testing.T) { - Setup() + th := Setup() + Client := th.CreateClient() team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} + user := &model.User{Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) + LinkUserToTeam(user, team) store.Must(Srv.Store.User().VerifyEmail(user.Id)) if utils.Cfg.FileSettings.DriverName != "" { @@ -455,13 +467,14 @@ func TestUserUploadProfileImage(t *testing.T) { body := &bytes.Buffer{} writer := multipart.NewWriter(body) - if _, upErr := Client.UploadFile("/users/newimage", body.Bytes(), writer.FormDataContentType()); upErr == nil { + if _, upErr := Client.UploadProfileFile(body.Bytes(), writer.FormDataContentType()); upErr == nil { t.Fatal("Should have errored") } Client.LoginByEmail(team.Name, user.Email, "pwd") + Client.SetTeamId(team.Id) - if _, upErr := Client.UploadFile("/users/newimage", body.Bytes(), writer.FormDataContentType()); upErr == nil { + if _, upErr := Client.UploadProfileFile(body.Bytes(), writer.FormDataContentType()); upErr == nil { t.Fatal("Should have errored") } @@ -486,7 +499,7 @@ func TestUserUploadProfileImage(t *testing.T) { t.Fatal(err) } - if _, upErr := Client.UploadFile("/users/newimage", body.Bytes(), writer.FormDataContentType()); upErr == nil { + if _, upErr := Client.UploadProfileFile(body.Bytes(), writer.FormDataContentType()); upErr == nil { t.Fatal("Should have errored") } @@ -512,7 +525,7 @@ func TestUserUploadProfileImage(t *testing.T) { t.Fatal(err) } - if _, upErr := Client.UploadFile("/users/newimage", body.Bytes(), writer.FormDataContentType()); upErr != nil { + if _, upErr := Client.UploadProfileFile(body.Bytes(), writer.FormDataContentType()); upErr != nil { t.Fatal(upErr) } @@ -526,11 +539,11 @@ func TestUserUploadProfileImage(t *testing.T) { s := s3.New(auth, aws.Regions[utils.Cfg.FileSettings.AmazonS3Region]) bucket := s.Bucket(utils.Cfg.FileSettings.AmazonS3Bucket) - if err := bucket.Del("teams/" + user.TeamId + "/users/" + user.Id + "/profile.png"); err != nil { + if err := bucket.Del("users/" + user.Id + "/profile.png"); err != nil { t.Fatal(err) } } else { - path := utils.Cfg.FileSettings.Directory + "teams/" + user.TeamId + "/users/" + user.Id + "/profile.png" + path := utils.Cfg.FileSettings.Directory + "users/" + user.Id + "/profile.png" if err := os.Remove(path); err != nil { t.Fatal("Couldn't remove file at " + path) } @@ -538,22 +551,24 @@ func TestUserUploadProfileImage(t *testing.T) { } else { body := &bytes.Buffer{} writer := multipart.NewWriter(body) - if _, upErr := Client.UploadFile("/users/newimage", body.Bytes(), writer.FormDataContentType()); upErr.StatusCode != http.StatusNotImplemented { + if _, upErr := Client.UploadProfileFile(body.Bytes(), writer.FormDataContentType()); upErr.StatusCode != http.StatusNotImplemented { t.Fatal("Should have failed with 501 - Not Implemented") } } } func TestUserUpdate(t *testing.T) { - Setup() + th := Setup() + Client := th.CreateClient() team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) time1 := model.GetMillis() - user := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd", LastActivityAt: time1, LastPingAt: time1, Roles: ""} + user := &model.User{Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd", LastActivityAt: time1, LastPingAt: time1, Roles: ""} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) + LinkUserToTeam(user, team) store.Must(Srv.Store.User().VerifyEmail(user.Id)) if _, err := Client.UpdateUser(user); err == nil { @@ -561,6 +576,7 @@ func TestUserUpdate(t *testing.T) { } Client.LoginByEmail(team.Name, user.Email, "pwd") + Client.SetTeamId(team.Id) time.Sleep(100 * time.Millisecond) @@ -569,7 +585,6 @@ func TestUserUpdate(t *testing.T) { time.Sleep(100 * time.Millisecond) user.Nickname = "Jim Jimmy" - user.TeamId = "12345678901234567890123456" user.LastActivityAt = time2 user.LastPingAt = time2 user.Roles = model.ROLE_TEAM_ADMIN @@ -581,9 +596,6 @@ func TestUserUpdate(t *testing.T) { if result.Data.(*model.User).Nickname != "Jim Jimmy" { t.Fatal("Nickname did not update properly") } - if result.Data.(*model.User).TeamId != team.Id { - t.Fatal("TeamId should not have updated") - } if result.Data.(*model.User).LastActivityAt == time2 { t.Fatal("LastActivityAt should not have updated") } @@ -598,21 +610,13 @@ func TestUserUpdate(t *testing.T) { } } - user.TeamId = "junk" - if _, err := Client.UpdateUser(user); err == nil { - t.Fatal("Should have errored - tried to change teamId to junk") - } - - user.TeamId = team.Id - if _, err := Client.UpdateUser(nil); err == nil { - t.Fatal("Should have errored") - } - - user2 := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} + user2 := &model.User{Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) + LinkUserToTeam(user2, team) store.Must(Srv.Store.User().VerifyEmail(user2.Id)) Client.LoginByEmail(team.Name, user2.Email, "pwd") + Client.SetTeamId(team.Id) user.Nickname = "Tim Timmy" @@ -622,13 +626,16 @@ func TestUserUpdate(t *testing.T) { } func TestUserUpdatePassword(t *testing.T) { - Setup() + th := Setup() + Client := th.CreateClient() team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) + Client.SetTeamId(team.Id) - user := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} + user := &model.User{Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) + LinkUserToTeam(user, team) store.Must(Srv.Store.User().VerifyEmail(user.Id)) if _, err := Client.UpdateUserPassword(user.Id, "pwd", "newpwd"); err == nil { @@ -670,8 +677,9 @@ func TestUserUpdatePassword(t *testing.T) { t.Fatal(err) } - user2 := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} + user2 := &model.User{Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) + LinkUserToTeam(user2, team) Client.LoginByEmail(team.Name, user2.Email, "pwd") @@ -681,17 +689,20 @@ func TestUserUpdatePassword(t *testing.T) { } func TestUserUpdateRoles(t *testing.T) { - Setup() + th := Setup() + Client := th.CreateClient() team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user := &model.User{TeamId: team.Id, Email: "test@nowhere.com", Nickname: "Corey Hulen", Password: "pwd"} + user := &model.User{Email: "success+" + model.NewId() + "@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) + LinkUserToTeam(user, team) store.Must(Srv.Store.User().VerifyEmail(user.Id)) - user2 := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} + user2 := &model.User{Email: "success+" + model.NewId() + "@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) + LinkUserToTeam(user2, team) store.Must(Srv.Store.User().VerifyEmail(user2.Id)) data := make(map[string]string) @@ -703,6 +714,7 @@ func TestUserUpdateRoles(t *testing.T) { } Client.LoginByEmail(team.Name, user2.Email, "pwd") + Client.SetTeamId(team.Id) if _, err := Client.UpdateUserRoles(data); err == nil { t.Fatal("Should have errored, not admin") @@ -711,11 +723,13 @@ func TestUserUpdateRoles(t *testing.T) { team2 := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team2 = Client.Must(Client.CreateTeam(team2)).Data.(*model.Team) - user3 := &model.User{TeamId: team2.Id, Email: "test@nowhere.com", Nickname: "Corey Hulen", Password: "pwd"} + user3 := &model.User{Email: "success+" + model.NewId() + "@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} user3 = Client.Must(Client.CreateUser(user3, "")).Data.(*model.User) + LinkUserToTeam(user3, team2) store.Must(Srv.Store.User().VerifyEmail(user3.Id)) Client.LoginByEmail(team2.Name, user3.Email, "pwd") + Client.SetTeamId(team2.Id) data["user_id"] = user2.Id @@ -740,27 +754,25 @@ func TestUserUpdateRoles(t *testing.T) { data["user_id"] = user2.Id - if result, err := Client.UpdateUserRoles(data); err != nil { - t.Log(data["new_roles"]) - t.Fatal(err) - } else { - if result.Data.(*model.User).Roles != "admin" { - t.Fatal("Roles did not update properly") - } + if _, err := Client.UpdateUserRoles(data); err == nil { + t.Fatal("Should have errored, bad role") } } func TestUserUpdateDeviceId(t *testing.T) { - Setup() + th := Setup() + Client := th.CreateClient() team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user := &model.User{TeamId: team.Id, Email: "test@nowhere.com", Nickname: "Corey Hulen", Password: "pwd"} + user := &model.User{Email: "success+" + model.NewId() + "@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) + LinkUserToTeam(user, team) store.Must(Srv.Store.User().VerifyEmail(user.Id)) Client.LoginByEmail(team.Name, user.Email, "pwd") + Client.SetTeamId(team.Id) deviceId := model.PUSH_NOTIFY_APPLE + ":1234567890" if _, err := Client.AttachDeviceId(deviceId); err != nil { @@ -779,17 +791,20 @@ func TestUserUpdateDeviceId(t *testing.T) { } func TestUserUpdateActive(t *testing.T) { - Setup() + th := Setup() + Client := th.CreateClient() team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user := &model.User{TeamId: team.Id, Email: "test@nowhere.com", Nickname: "Corey Hulen", Password: "pwd"} + user := &model.User{Email: "success+" + model.NewId() + "@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) + LinkUserToTeam(user, team) store.Must(Srv.Store.User().VerifyEmail(user.Id)) - user2 := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} + user2 := &model.User{Email: "success+" + model.NewId() + "@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) + LinkUserToTeam(user2, team) store.Must(Srv.Store.User().VerifyEmail(user2.Id)) if _, err := Client.UpdateActive(user.Id, false); err == nil { @@ -797,25 +812,31 @@ func TestUserUpdateActive(t *testing.T) { } Client.LoginByEmail(team.Name, user2.Email, "pwd") + Client.SetTeamId(team.Id) if _, err := Client.UpdateActive(user.Id, false); err == nil { t.Fatal("Should have errored, not admin") } + Client.Must(Client.Logout()) + team2 := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team2 = Client.Must(Client.CreateTeam(team2)).Data.(*model.Team) - user3 := &model.User{TeamId: team2.Id, Email: "test@nowhere.com", Nickname: "Corey Hulen", Password: "pwd"} + user3 := &model.User{Email: "success+" + model.NewId() + "@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} user3 = Client.Must(Client.CreateUser(user3, "")).Data.(*model.User) + LinkUserToTeam(user2, team2) store.Must(Srv.Store.User().VerifyEmail(user3.Id)) Client.LoginByEmail(team2.Name, user3.Email, "pwd") + Client.SetTeamId(team2.Id) if _, err := Client.UpdateActive(user.Id, false); err == nil { - t.Fatal("Should have errored, wrong team") + t.Fatal("Should have errored, not yourself") } Client.LoginByEmail(team.Name, user.Email, "pwd") + Client.SetTeamId(team.Id) if _, err := Client.UpdateActive("junk", false); err == nil { t.Fatal("Should have errored, bad id") @@ -824,35 +845,22 @@ func TestUserUpdateActive(t *testing.T) { if _, err := Client.UpdateActive("12345678901234567890123456", false); err == nil { t.Fatal("Should have errored, bad id") } - - if result, err := Client.UpdateActive(user2.Id, false); err != nil { - t.Fatal(err) - } else { - if result.Data.(*model.User).DeleteAt == 0 { - t.Fatal("active did not update properly") - } - } - - if result, err := Client.UpdateActive(user2.Id, true); err != nil { - t.Fatal(err) - } else { - if result.Data.(*model.User).DeleteAt != 0 { - t.Fatal("active did not update properly true") - } - } } func TestUserPermDelete(t *testing.T) { - Setup() + th := Setup() + Client := th.CreateClient() team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} + user1 := &model.User{Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) + LinkUserToTeam(user1, team) store.Must(Srv.Store.User().VerifyEmail(user1.Id)) Client.LoginByEmail(team.Name, user1.Email, "pwd") + Client.SetTeamId(team.Id) channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) @@ -882,161 +890,117 @@ func TestUserPermDelete(t *testing.T) { } func TestSendPasswordReset(t *testing.T) { - Setup() + th := Setup() + Client := th.CreateClient() team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} + user := &model.User{Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) + LinkUserToTeam(user, team) store.Must(Srv.Store.User().VerifyEmail(user.Id)) - data := make(map[string]string) - data["email"] = user.Email - data["name"] = team.Name - - if _, err := Client.SendPasswordReset(data); err != nil { + if _, err := Client.SendPasswordReset(user.Email); err != nil { t.Fatal(err) } - data["email"] = "" - if _, err := Client.SendPasswordReset(data); err == nil { + if _, err := Client.SendPasswordReset(""); err == nil { t.Fatal("Should have errored - no email") } - data["email"] = "junk@junk.com" - if _, err := Client.SendPasswordReset(data); err == nil { + if _, err := Client.SendPasswordReset("junk@junk.com"); err == nil { t.Fatal("Should have errored - bad email") } - data["email"] = user.Email - data["name"] = "" - if _, err := Client.SendPasswordReset(data); err == nil { - t.Fatal("Should have errored - no name") - } - - data["name"] = "junk" - if _, err := Client.SendPasswordReset(data); err == nil { - t.Fatal("Should have errored - bad name") - } - - user2 := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", AuthData: "1", AuthService: "random"} + user2 := &model.User{Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", AuthData: "1", AuthService: "random"} user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) + LinkUserToTeam(user2, team) store.Must(Srv.Store.User().VerifyEmail(user2.Id)) - data["email"] = user2.Email - data["name"] = team.Name - if _, err := Client.SendPasswordReset(data); err == nil { + if _, err := Client.SendPasswordReset(user2.Email); err == nil { t.Fatal("should have errored - SSO user can't send reset password link") } } func TestResetPassword(t *testing.T) { - Setup() + th := Setup().InitSystemAdmin() + Client := th.SystemAdminClient + team := th.SystemAdminTeam - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} + user := &model.User{Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) + LinkUserToTeam(user, team) store.Must(Srv.Store.User().VerifyEmail(user.Id)) - data := make(map[string]string) - data["new_password"] = "newpwd" - props := make(map[string]string) - props["user_id"] = user.Id - props["time"] = fmt.Sprintf("%v", model.GetMillis()) - data["data"] = model.MapToJson(props) - data["hash"] = model.HashPassword(fmt.Sprintf("%v:%v", data["data"], utils.Cfg.EmailSettings.PasswordResetSalt)) - data["name"] = team.Name + Client.Must(Client.SendPasswordReset(user.Email)) - if _, err := Client.ResetPassword(data); err != nil { - t.Fatal(err) + var recovery *model.PasswordRecovery + if result := <-Srv.Store.PasswordRecovery().Get(user.Id); result.Err != nil { + t.Fatal(result.Err) + } else { + recovery = result.Data.(*model.PasswordRecovery) } - data["new_password"] = "" - if _, err := Client.ResetPassword(data); err == nil { + if _, err := Client.ResetPassword(recovery.Code, ""); err == nil { t.Fatal("Should have errored - no password") } - data["new_password"] = "npwd" - if _, err := Client.ResetPassword(data); err == nil { + if _, err := Client.ResetPassword(recovery.Code, "newp"); err == nil { t.Fatal("Should have errored - password too short") } - data["new_password"] = "newpwd" - data["hash"] = "" - if _, err := Client.ResetPassword(data); err == nil { - t.Fatal("Should have errored - no hash") + if _, err := Client.ResetPassword("", "newpwd"); err == nil { + t.Fatal("Should have errored - no code") } - data["hash"] = "junk" - if _, err := Client.ResetPassword(data); err == nil { - t.Fatal("Should have errored - bad hash") + if _, err := Client.ResetPassword("junk", "newpwd"); err == nil { + t.Fatal("Should have errored - bad code") } - props["user_id"] = "" - data["data"] = model.MapToJson(props) - if _, err := Client.ResetPassword(data); err == nil { - t.Fatal("Should have errored - no user id") + code := "" + for i := 0; i < model.PASSWORD_RECOVERY_CODE_SIZE; i++ { + code += "a" } - - data["user_id"] = "12345678901234567890123456" - data["data"] = model.MapToJson(props) - if _, err := Client.ResetPassword(data); err == nil { - t.Fatal("Should have errored - bad user id") - } - - props["user_id"] = user.Id - props["time"] = "" - data["data"] = model.MapToJson(props) - if _, err := Client.ResetPassword(data); err == nil { - t.Fatal("Should have errored - no time") + if _, err := Client.ResetPassword(code, "newpwd"); err == nil { + t.Fatal("Should have errored - bad code") } - props["time"] = fmt.Sprintf("%v", model.GetMillis()) - data["data"] = model.MapToJson(props) - data["domain"] = "" - if _, err := Client.ResetPassword(data); err == nil { - t.Fatal("Should have errored - no domain") + if _, err := Client.ResetPassword(recovery.Code, "newpwd"); err != nil { + t.Fatal(err) } - data["domain"] = "junk" - if _, err := Client.ResetPassword(data); err == nil { - t.Fatal("Should have errored - bad domain") - } + Client.Logout() + Client.Must(Client.LoginById(user.Id, "newpwd")) + Client.SetTeamId(team.Id) - team2 := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test2@nowhere.com", Type: model.TEAM_OPEN} - team2 = Client.Must(Client.CreateTeam(team2)).Data.(*model.Team) + Client.Must(Client.SendPasswordReset(user.Email)) - data["domain"] = team2.Name - if _, err := Client.ResetPassword(data); err == nil { - t.Fatal("Should have errored - domain team doesn't match user team") + if result := <-Srv.Store.PasswordRecovery().Get(user.Id); result.Err != nil { + t.Fatal(result.Err) + } else { + recovery = result.Data.(*model.PasswordRecovery) } - user2 := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", AuthData: "1", AuthService: "random"} - user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user2.Id)) + if result := <-Srv.Store.User().UpdateAuthData(user.Id, "random", "1", ""); result.Err != nil { + t.Fatal(result.Err) + } - data["new_password"] = "newpwd" - props["user_id"] = user2.Id - props["time"] = fmt.Sprintf("%v", model.GetMillis()) - data["data"] = model.MapToJson(props) - data["hash"] = model.HashPassword(fmt.Sprintf("%v:%v", data["data"], utils.Cfg.EmailSettings.PasswordResetSalt)) - data["name"] = team.Name - if _, err := Client.ResetPassword(data); err == nil { - t.Fatal("should have errored - SSO user can't reset password") + if _, err := Client.ResetPassword(recovery.Code, "newpwd"); err == nil { + t.Fatal("Should have errored - sso user") } } func TestUserUpdateNotify(t *testing.T) { - Setup() + th := Setup() + Client := th.CreateClient() team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd", Roles: ""} + user := &model.User{Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd", Roles: ""} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) + LinkUserToTeam(user, team) store.Must(Srv.Store.User().VerifyEmail(user.Id)) data := make(map[string]string) @@ -1050,6 +1014,7 @@ func TestUserUpdateNotify(t *testing.T) { } Client.LoginByEmail(team.Name, user.Email, "pwd") + Client.SetTeamId(team.Id) if result, err := Client.UpdateUserNotify(data); err != nil { t.Fatal(err) @@ -1099,7 +1064,8 @@ func TestUserUpdateNotify(t *testing.T) { } func TestFuzzyUserCreate(t *testing.T) { - Setup() + th := Setup() + Client := th.CreateClient() team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} rteam, _ := Client.CreateTeam(&team) @@ -1115,30 +1081,36 @@ func TestFuzzyUserCreate(t *testing.T) { testEmail = utils.FUZZY_STRINGS_EMAILS[i] } - user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + testEmail, Nickname: testName, Password: "hello"} + user := model.User{Email: strings.ToLower(model.NewId()) + testEmail, Nickname: testName, Password: "hello"} - _, err := Client.CreateUser(&user, "") + ruser, err := Client.CreateUser(&user, "") if err != nil { t.Fatal(err) } + + LinkUserToTeam(ruser.Data.(*model.User), rteam.Data.(*model.Team)) } } func TestStatuses(t *testing.T) { - Setup() + th := Setup() + Client := th.CreateClient() team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} rteam, _ := Client.CreateTeam(&team) - user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} + user := model.User{Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} ruser := Client.Must(Client.CreateUser(&user, "")).Data.(*model.User) + LinkUserToTeam(ruser, rteam.Data.(*model.Team)) store.Must(Srv.Store.User().VerifyEmail(ruser.Id)) - user2 := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} + user2 := model.User{Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} ruser2 := Client.Must(Client.CreateUser(&user2, "")).Data.(*model.User) + LinkUserToTeam(ruser2, rteam.Data.(*model.Team)) store.Must(Srv.Store.User().VerifyEmail(ruser2.Id)) Client.LoginByEmail(team.Name, user.Email, user.Password) + Client.SetTeamId(team.Id) userIds := []string{ruser2.Id} @@ -1150,6 +1122,7 @@ func TestStatuses(t *testing.T) { statuses := r1.Data.(map[string]string) if len(statuses) != 1 { + t.Log(statuses) t.Fatal("invalid number of statuses") } @@ -1162,13 +1135,15 @@ func TestStatuses(t *testing.T) { } func TestEmailToOAuth(t *testing.T) { - Setup() + th := Setup() + Client := th.CreateClient() team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} rteam, _ := Client.CreateTeam(&team) - user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} + user := model.User{Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} ruser := Client.Must(Client.CreateUser(&user, "")).Data.(*model.User) + LinkUserToTeam(ruser, rteam.Data.(*model.Team)) store.Must(Srv.Store.User().VerifyEmail(ruser.Id)) m := map[string]string{} @@ -1211,17 +1186,20 @@ func TestEmailToOAuth(t *testing.T) { } func TestOAuthToEmail(t *testing.T) { - Setup() + th := Setup() + Client := th.CreateClient() team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} rteam, _ := Client.CreateTeam(&team) - user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} + user := model.User{Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} ruser := Client.Must(Client.CreateUser(&user, "")).Data.(*model.User) + LinkUserToTeam(ruser, rteam.Data.(*model.Team)) store.Must(Srv.Store.User().VerifyEmail(ruser.Id)) - user2 := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} + user2 := model.User{Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} ruser2 := Client.Must(Client.CreateUser(&user2, "")).Data.(*model.User) + LinkUserToTeam(ruser2, rteam.Data.(*model.Team)) store.Must(Srv.Store.User().VerifyEmail(ruser2.Id)) m := map[string]string{} @@ -1246,12 +1224,6 @@ func TestOAuthToEmail(t *testing.T) { t.Fatal("should have failed - missing email") } - m["email"] = ruser.Email - m["team_name"] = "junk" - if _, err := Client.OAuthToEmail(m); err == nil { - t.Fatal("should have failed - bad team name") - } - m["team_name"] = team.Name m["email"] = "junk" if _, err := Client.OAuthToEmail(m); err == nil { @@ -1265,13 +1237,15 @@ func TestOAuthToEmail(t *testing.T) { } func TestLDAPToEmail(t *testing.T) { - Setup() + th := Setup() + Client := th.CreateClient() team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} rteam, _ := Client.CreateTeam(&team) - user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} + user := model.User{Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} ruser := Client.Must(Client.CreateUser(&user, "")).Data.(*model.User) + LinkUserToTeam(ruser, rteam.Data.(*model.Team)) store.Must(Srv.Store.User().VerifyEmail(ruser.Id)) Client.LoginByEmail(team.Name, user.Email, user.Password) @@ -1316,13 +1290,15 @@ func TestLDAPToEmail(t *testing.T) { } func TestEmailToLDAP(t *testing.T) { - Setup() + th := Setup() + Client := th.CreateClient() team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} rteam, _ := Client.CreateTeam(&team) - user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} + user := model.User{Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} ruser := Client.Must(Client.CreateUser(&user, "")).Data.(*model.User) + LinkUserToTeam(ruser, rteam.Data.(*model.Team)) store.Must(Srv.Store.User().VerifyEmail(ruser.Id)) Client.LoginByEmail(team.Name, user.Email, user.Password) @@ -1377,49 +1353,83 @@ func TestEmailToLDAP(t *testing.T) { } } -func TestMeLoggedIn(t *testing.T) { - Setup() +func TestMeInitialLoad(t *testing.T) { + th := Setup().InitBasic() - team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - rteam, _ := Client.CreateTeam(&team) + if result, err := th.BasicClient.GetInitialLoad(); err != nil { + t.Fatal(err) + } else { + il := result.Data.(*model.InitialLoad) - user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - ruser := Client.Must(Client.CreateUser(&user, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(ruser.Id)) + if il.User == nil { + t.Fatal("should be valid") + } - Client.AuthToken = "invalid" + if il.Preferences == nil { + t.Fatal("should be valid") + } - if result, err := Client.GetMeLoggedIn(); err != nil { - t.Fatal(err) - } else { - meLoggedIn := result.Data.(map[string]string) + if len(il.Teams) != 1 { + t.Fatal("should be valid") + } - if val, ok := meLoggedIn["logged_in"]; !ok || val != "false" { - t.Fatal("Got: " + val) + if len(il.TeamMembers) != 1 { + t.Fatal("should be valid") + } + + if len(il.ClientCfg) == 0 { + t.Fatal("should be valid") + } + + if len(il.LicenseCfg) == 0 { + t.Fatal("should be valid") } } - Client.LoginByEmail(team.Name, user.Email, user.Password) + th.BasicClient.Logout() - if result, err := Client.GetMeLoggedIn(); err != nil { + if result, err := th.BasicClient.GetInitialLoad(); err != nil { t.Fatal(err) } else { - meLoggedIn := result.Data.(map[string]string) + il := result.Data.(*model.InitialLoad) + + if il.User != nil { + t.Fatal("should be valid") + } - if val, ok := meLoggedIn["logged_in"]; !ok || val != "true" { - t.Fatal("Got: " + val) + if il.Preferences != nil { + t.Fatal("should be valid") + } + + if len(il.Teams) != 0 { + t.Fatal("should be valid") + } + + if len(il.TeamMembers) != 0 { + t.Fatal("should be valid") + } + + if len(il.ClientCfg) == 0 { + t.Fatal("should be valid") + } + + if len(il.LicenseCfg) == 0 { + t.Fatal("should be valid") } } + } func TestGenerateMfaQrCode(t *testing.T) { - Setup() + th := Setup() + Client := th.CreateClient() team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} rteam, _ := Client.CreateTeam(&team) - user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} + user := model.User{Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} ruser, _ := Client.CreateUser(&user, "") + LinkUserToTeam(ruser.Data.(*model.User), rteam.Data.(*model.Team)) store.Must(Srv.Store.User().VerifyEmail(ruser.Data.(*model.User).Id)) Client.Logout() @@ -1438,13 +1448,26 @@ func TestGenerateMfaQrCode(t *testing.T) { } func TestUpdateMfa(t *testing.T) { - Setup() + th := Setup() + Client := th.CreateClient() + + if utils.License.Features.MFA == nil { + utils.License.Features.MFA = new(bool) + } + + enableMfa := *utils.Cfg.ServiceSettings.EnableMultifactorAuthentication + defer func() { + utils.IsLicensed = false + *utils.License.Features.MFA = false + *utils.Cfg.ServiceSettings.EnableMultifactorAuthentication = enableMfa + }() team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} rteam, _ := Client.CreateTeam(&team) - user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} + user := model.User{Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} ruser, _ := Client.CreateUser(&user, "") + LinkUserToTeam(ruser.Data.(*model.User), rteam.Data.(*model.Team)) store.Must(Srv.Store.User().VerifyEmail(ruser.Data.(*model.User).Id)) Client.Logout() @@ -1463,20 +1486,30 @@ func TestUpdateMfa(t *testing.T) { t.Fatal("should have failed - not licensed") } - // need to add more test cases when license and config can be configured for tests + utils.IsLicensed = true + *utils.License.Features.MFA = true + *utils.Cfg.ServiceSettings.EnableMultifactorAuthentication = true + + if _, err := Client.UpdateMfa(true, "123456"); err == nil { + t.Fatal("should have failed - bad token") + } + + // need to add more test cases when enterprise bits can be loaded into tests } func TestCheckMfa(t *testing.T) { - Setup() + th := Setup() + Client := th.CreateClient() team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} rteam, _ := Client.CreateTeam(&team) - user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} + user := model.User{Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} ruser, _ := Client.CreateUser(&user, "") + LinkUserToTeam(ruser.Data.(*model.User), rteam.Data.(*model.Team)) store.Must(Srv.Store.User().VerifyEmail(ruser.Data.(*model.User).Id)) - if result, err := Client.CheckMfa(model.USER_AUTH_SERVICE_EMAIL, team.Name, user.Email); err != nil { + if result, err := Client.CheckMfa(model.USER_AUTH_SERVICE_EMAIL, user.Email); err != nil { t.Fatal(err) } else { resp := result.Data.(map[string]string) @@ -1485,5 +1518,5 @@ func TestCheckMfa(t *testing.T) { } } - // need to add more test cases when license and config can be configured for tests + // need to add more test cases when enterprise bits can be loaded into tests } diff --git a/api/web_conn.go b/api/web_conn.go index 515a8ab31..397af0696 100644 --- a/api/web_conn.go +++ b/api/web_conn.go @@ -7,7 +7,6 @@ import ( l4g "github.com/alecthomas/log4go" "github.com/gorilla/websocket" "github.com/mattermost/platform/model" - "github.com/mattermost/platform/store" "github.com/mattermost/platform/utils" "time" ) @@ -21,14 +20,15 @@ const ( ) type WebConn struct { - WebSocket *websocket.Conn - Send chan *model.Message - TeamId string - UserId string - ChannelAccessCache map[string]bool + WebSocket *websocket.Conn + Send chan *model.Message + SessionId string + UserId string + hasPermissionsToChannel map[string]bool + hasPermissionsToTeam map[string]bool } -func NewWebConn(ws *websocket.Conn, teamId string, userId string, sessionId string) *WebConn { +func NewWebConn(ws *websocket.Conn, userId string, sessionId string) *WebConn { go func() { achan := Srv.Store.User().UpdateUserAndSessionActivity(userId, sessionId, model.GetMillis()) pchan := Srv.Store.User().UpdateLastPingAt(userId, model.GetMillis()) @@ -42,7 +42,14 @@ func NewWebConn(ws *websocket.Conn, teamId string, userId string, sessionId stri } }() - return &WebConn{Send: make(chan *model.Message, 64), WebSocket: ws, UserId: userId, TeamId: teamId, ChannelAccessCache: make(map[string]bool)} + return &WebConn{ + Send: make(chan *model.Message, 64), + WebSocket: ws, + UserId: userId, + SessionId: sessionId, + hasPermissionsToChannel: make(map[string]bool), + hasPermissionsToTeam: make(map[string]bool), + } } func (c *WebConn) readPump() { @@ -69,7 +76,6 @@ func (c *WebConn) readPump() { if err := c.WebSocket.ReadJSON(&msg); err != nil { return } else { - msg.TeamId = c.TeamId msg.UserId = c.UserId PublishAndForget(&msg) } @@ -107,19 +113,53 @@ func (c *WebConn) writePump() { } } -func (c *WebConn) updateChannelAccessCache(channelId string) bool { - allowed := hasPermissionsToChannel(Srv.Store.Channel().CheckPermissionsTo(c.TeamId, channelId, c.UserId)) - c.ChannelAccessCache[channelId] = allowed +func (c *WebConn) InvalidateCache() { + c.hasPermissionsToChannel = make(map[string]bool) + c.hasPermissionsToTeam = make(map[string]bool) +} + +func (c *WebConn) HasPermissionsToTeam(teamId string) bool { + perm, ok := c.hasPermissionsToTeam[teamId] + if !ok { + session := GetSession(c.SessionId) + if session == nil { + perm = false + c.hasPermissionsToTeam[teamId] = perm + } else { + member := session.GetTeamByTeamId(teamId) + + if member != nil { + perm = true + c.hasPermissionsToTeam[teamId] = perm + } else { + perm = true + c.hasPermissionsToTeam[teamId] = perm + } - return allowed + } + } + + return perm } -func hasPermissionsToChannel(sc store.StoreChannel) bool { - if cresult := <-sc; cresult.Err != nil { - return false - } else if cresult.Data.(int64) != 1 { - return false +func (c *WebConn) HasPermissionsToChannel(channelId string) bool { + perm, ok := c.hasPermissionsToChannel[channelId] + if !ok { + if cresult := <-Srv.Store.Channel().CheckPermissionsToNoTeam(channelId, c.UserId); cresult.Err != nil { + perm = false + c.hasPermissionsToChannel[channelId] = perm + } else { + count := cresult.Data.(int64) + + if count == 1 { + perm = true + c.hasPermissionsToChannel[channelId] = perm + } else { + perm = false + c.hasPermissionsToChannel[channelId] = perm + } + } } - return true + return perm } diff --git a/api/web_hub.go b/api/web_hub.go index 5fe9d6ae8..241ebcef0 100644 --- a/api/web_hub.go +++ b/api/web_hub.go @@ -10,19 +10,21 @@ import ( ) type Hub struct { - teamHubs map[string]*TeamHub - register chan *WebConn - unregister chan *WebConn - broadcast chan *model.Message - stop chan string + connections map[*WebConn]bool + register chan *WebConn + unregister chan *WebConn + broadcast chan *model.Message + stop chan string + invalidateUser chan string } var hub = &Hub{ - register: make(chan *WebConn), - unregister: make(chan *WebConn), - teamHubs: make(map[string]*TeamHub), - broadcast: make(chan *model.Message), - stop: make(chan string), + register: make(chan *WebConn), + unregister: make(chan *WebConn), + connections: make(map[*WebConn]bool), + broadcast: make(chan *model.Message), + stop: make(chan string), + invalidateUser: make(chan string), } func PublishAndForget(message *model.Message) { @@ -31,16 +33,8 @@ func PublishAndForget(message *model.Message) { }() } -func UpdateChannelAccessCache(teamId, userId, channelId string) { - if nh, ok := hub.teamHubs[teamId]; ok { - nh.UpdateChannelAccessCache(userId, channelId) - } -} - -func UpdateChannelAccessCacheAndForget(teamId, userId, channelId string) { - go func() { - UpdateChannelAccessCache(teamId, userId, channelId) - }() +func InvalidateCacheForUser(userId string) { + hub.invalidateUser <- userId } func (h *Hub) Register(webConn *WebConn) { @@ -65,34 +59,92 @@ func (h *Hub) Start() { go func() { for { select { + case webCon := <-h.register: + h.connections[webCon] = true - case c := <-h.register: - nh := h.teamHubs[c.TeamId] - - if nh == nil { - nh = NewTeamHub(c.TeamId) - h.teamHubs[c.TeamId] = nh - nh.Start() + case webCon := <-h.unregister: + if _, ok := h.connections[webCon]; ok { + delete(h.connections, webCon) + close(webCon.Send) } - - nh.Register(c) - - case c := <-h.unregister: - if nh, ok := h.teamHubs[c.TeamId]; ok { - nh.Unregister(c) + case userId := <-h.invalidateUser: + for webCon := range h.connections { + if webCon.UserId == userId { + webCon.InvalidateCache() + } } + case msg := <-h.broadcast: - nh := h.teamHubs[msg.TeamId] - if nh != nil { - nh.broadcast <- msg + for webCon := range h.connections { + if shouldSendEvent(webCon, msg) { + select { + case webCon.Send <- msg: + default: + close(webCon.Send) + delete(h.connections, webCon) + } + } } + case s := <-h.stop: l4g.Debug(utils.T("api.web_hub.start.stopping.debug"), s) - for _, v := range h.teamHubs { - v.Stop() + + for webCon := range h.connections { + webCon.WebSocket.Close() } + return } } }() } + +func shouldSendEvent(webCon *WebConn, msg *model.Message) bool { + + if webCon.UserId == msg.UserId { + // Don't need to tell the user they are typing + if msg.Action == model.ACTION_TYPING { + return false + } + + // We have to make sure the user is in the channel. Otherwise system messages that + // post about users in channels they are not in trigger warnings. + if len(msg.ChannelId) > 0 { + allowed := webCon.HasPermissionsToChannel(msg.ChannelId) + + if !allowed { + return false + } + } + } else { + // Don't share a user's view or preference events with other users + if msg.Action == model.ACTION_CHANNEL_VIEWED { + return false + } else if msg.Action == model.ACTION_PREFERENCE_CHANGED { + return false + } else if msg.Action == model.ACTION_EPHEMERAL_MESSAGE { + // For now, ephemeral messages are sent directly to individual users + return false + } + + // Only report events to users who are in the team for the event + if len(msg.TeamId) > 0 { + allowed := webCon.HasPermissionsToTeam(msg.TeamId) + + if !allowed { + return false + } + } + + // Only report events to users who are in the channel for the event + if len(msg.ChannelId) > 0 { + allowed := webCon.HasPermissionsToChannel(msg.ChannelId) + + if !allowed { + return false + } + } + } + + return true +} diff --git a/api/web_socket.go b/api/web_socket.go index e15732f43..72a9c61a6 100644 --- a/api/web_socket.go +++ b/api/web_socket.go @@ -5,16 +5,15 @@ package api import ( l4g "github.com/alecthomas/log4go" - "github.com/gorilla/mux" "github.com/gorilla/websocket" "github.com/mattermost/platform/model" "github.com/mattermost/platform/utils" "net/http" ) -func InitWebSocket(r *mux.Router) { +func InitWebSocket() { l4g.Debug(utils.T("api.web_socket.init.debug")) - r.Handle("/websocket", ApiUserRequiredTrustRequester(connect)).Methods("GET") + BaseRoutes.Users.Handle("/websocket", ApiUserRequiredTrustRequester(connect)).Methods("GET") hub.Start() } @@ -34,7 +33,7 @@ func connect(c *Context, w http.ResponseWriter, r *http.Request) { return } - wc := NewWebConn(ws, c.Session.TeamId, c.Session.UserId, c.Session.Id) + wc := NewWebConn(ws, c.Session.UserId, c.Session.Id) hub.Register(wc) go wc.writePump() wc.readPump() diff --git a/api/web_socket_test.go b/api/web_socket_test.go index 2c0ac61eb..7cb04e93e 100644 --- a/api/web_socket_test.go +++ b/api/web_socket_test.go @@ -6,7 +6,6 @@ package api import ( "github.com/gorilla/websocket" "github.com/mattermost/platform/model" - "github.com/mattermost/platform/store" "github.com/mattermost/platform/utils" "net/http" "testing" @@ -14,22 +13,14 @@ import ( ) func TestSocket(t *testing.T) { - Setup() + th := Setup().InitBasic() + Client := th.BasicClient + team := th.BasicTeam + channel1 := th.BasicChannel + channel2 := th.CreateChannel(Client, team) + Client.Must(Client.AddChannelMember(channel1.Id, th.BasicUser2.Id)) - url := "ws://localhost" + utils.Cfg.ServiceSettings.ListenAddress + "/api/v1/websocket" - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - Client.LoginByEmail(team.Name, user1.Email, "pwd") - - channel1 := &model.Channel{DisplayName: "Test Web Scoket 1", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} - channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) - - channel2 := &model.Channel{DisplayName: "Test Web Scoket 2", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} - channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel) + url := "ws://localhost" + utils.Cfg.ServiceSettings.ListenAddress + model.API_URL_SUFFIX + "/users/websocket" header1 := http.Header{} header1.Set(model.HEADER_AUTH, "BEARER "+Client.AuthToken) @@ -39,10 +30,7 @@ func TestSocket(t *testing.T) { t.Fatal(err) } - user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user2.Id)) - Client.LoginByEmail(team.Name, user2.Email, "pwd") + th.LoginBasic2() header2 := http.Header{} header2.Set(model.HEADER_AUTH, "BEARER "+Client.AuthToken) @@ -53,21 +41,11 @@ func TestSocket(t *testing.T) { } time.Sleep(300 * time.Millisecond) - Client.Must(Client.JoinChannel(channel1.Id)) - // Read the user_added message that gets generated var rmsg model.Message - if err := c2.ReadJSON(&rmsg); err != nil { - t.Fatal(err) - } - - // Read the second user_added message that gets generated - if err := c2.ReadJSON(&rmsg); err != nil { - t.Fatal(err) - } // Test sending message without a channelId - m := model.NewMessage("", "", "", model.ACTION_TYPING) + m := model.NewMessage(team.Id, "", "", model.ACTION_TYPING) m.Add("RootId", model.NewId()) m.Add("ParentId", model.NewId()) @@ -77,6 +55,8 @@ func TestSocket(t *testing.T) { t.Fatal(err) } + t.Log(rmsg.ToJson()) + if team.Id != rmsg.TeamId { t.Fatal("Ids do not match") } @@ -86,7 +66,7 @@ func TestSocket(t *testing.T) { } // Test sending messsage to Channel you have access to - m = model.NewMessage("", channel1.Id, "", model.ACTION_TYPING) + m = model.NewMessage(team.Id, channel1.Id, "", model.ACTION_TYPING) m.Add("RootId", model.NewId()) m.Add("ParentId", model.NewId()) diff --git a/api/web_team_hub.go b/api/web_team_hub.go deleted file mode 100644 index 9d1c56f15..000000000 --- a/api/web_team_hub.go +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -package api - -import ( - l4g "github.com/alecthomas/log4go" - "github.com/mattermost/platform/model" - "github.com/mattermost/platform/utils" -) - -type TeamHub struct { - connections map[*WebConn]bool - broadcast chan *model.Message - register chan *WebConn - unregister chan *WebConn - stop chan bool - teamId string -} - -func NewTeamHub(teamId string) *TeamHub { - return &TeamHub{ - broadcast: make(chan *model.Message), - register: make(chan *WebConn), - unregister: make(chan *WebConn), - connections: make(map[*WebConn]bool), - stop: make(chan bool), - teamId: teamId, - } -} - -func (h *TeamHub) Register(webConn *WebConn) { - h.register <- webConn -} - -func (h *TeamHub) Unregister(webConn *WebConn) { - h.unregister <- webConn -} - -func (h *TeamHub) Stop() { - h.stop <- true -} - -func (h *TeamHub) Start() { - go func() { - for { - select { - case webCon := <-h.register: - h.connections[webCon] = true - case webCon := <-h.unregister: - if _, ok := h.connections[webCon]; ok { - delete(h.connections, webCon) - close(webCon.Send) - } - case msg := <-h.broadcast: - for webCon := range h.connections { - if ShouldSendEvent(webCon, msg) { - select { - case webCon.Send <- msg: - default: - close(webCon.Send) - delete(h.connections, webCon) - } - } - } - case s := <-h.stop: - if s { - - l4g.Debug(utils.T("api.web_team_hun.start.debug"), h.teamId) - - for webCon := range h.connections { - webCon.WebSocket.Close() - } - - return - } - } - } - }() -} - -func (h *TeamHub) UpdateChannelAccessCache(userId string, channelId string) { - for webCon := range h.connections { - if webCon.UserId == userId { - webCon.updateChannelAccessCache(channelId) - break - } - } -} - -func ShouldSendEvent(webCon *WebConn, msg *model.Message) bool { - - if webCon.UserId == msg.UserId { - // Don't need to tell the user they are typing - if msg.Action == model.ACTION_TYPING { - return false - } - } else { - // Don't share a user's view or preference events with other users - if msg.Action == model.ACTION_CHANNEL_VIEWED { - return false - } else if msg.Action == model.ACTION_PREFERENCE_CHANGED { - return false - } else if msg.Action == model.ACTION_EPHEMERAL_MESSAGE { - // For now, ephemeral messages are sent directly to individual users - return false - } - - // Only report events to a user who is the subject of the event, or is in the channel of the event - if len(msg.ChannelId) > 0 { - allowed, ok := webCon.ChannelAccessCache[msg.ChannelId] - if !ok { - allowed = webCon.updateChannelAccessCache(msg.ChannelId) - } - - if !allowed { - return false - } - } - } - - return true -} diff --git a/api/webhook.go b/api/webhook.go index fe1aa1175..ea628e39c 100644 --- a/api/webhook.go +++ b/api/webhook.go @@ -14,20 +14,19 @@ import ( "github.com/mattermost/platform/utils" ) -func InitWebhook(r *mux.Router) { +func InitWebhook() { l4g.Debug(utils.T("api.webhook.init.debug")) - sr := r.PathPrefix("/hooks").Subrouter() - sr.Handle("/incoming/create", ApiUserRequired(createIncomingHook)).Methods("POST") - sr.Handle("/incoming/delete", ApiUserRequired(deleteIncomingHook)).Methods("POST") - sr.Handle("/incoming/list", ApiUserRequired(getIncomingHooks)).Methods("GET") + BaseRoutes.Hooks.Handle("/incoming/create", ApiUserRequired(createIncomingHook)).Methods("POST") + BaseRoutes.Hooks.Handle("/incoming/delete", ApiUserRequired(deleteIncomingHook)).Methods("POST") + BaseRoutes.Hooks.Handle("/incoming/list", ApiUserRequired(getIncomingHooks)).Methods("GET") - sr.Handle("/outgoing/create", ApiUserRequired(createOutgoingHook)).Methods("POST") - sr.Handle("/outgoing/regen_token", ApiUserRequired(regenOutgoingHookToken)).Methods("POST") - sr.Handle("/outgoing/delete", ApiUserRequired(deleteOutgoingHook)).Methods("POST") - sr.Handle("/outgoing/list", ApiUserRequired(getOutgoingHooks)).Methods("GET") + BaseRoutes.Hooks.Handle("/outgoing/create", ApiUserRequired(createOutgoingHook)).Methods("POST") + BaseRoutes.Hooks.Handle("/outgoing/regen_token", ApiUserRequired(regenOutgoingHookToken)).Methods("POST") + BaseRoutes.Hooks.Handle("/outgoing/delete", ApiUserRequired(deleteOutgoingHook)).Methods("POST") + BaseRoutes.Hooks.Handle("/outgoing/list", ApiUserRequired(getOutgoingHooks)).Methods("GET") - sr.Handle("/{id:[A-Za-z0-9]+}", ApiAppHandler(incomingWebhook)).Methods("POST") + BaseRoutes.Hooks.Handle("/{id:[A-Za-z0-9]+}", ApiAppHandler(incomingWebhook)).Methods("POST") // Old route. Remove eventually. mr := Srv.Router @@ -59,10 +58,10 @@ func createIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) { } cchan := Srv.Store.Channel().Get(hook.ChannelId) - pchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, hook.ChannelId, c.Session.UserId) + pchan := Srv.Store.Channel().CheckPermissionsTo(c.TeamId, hook.ChannelId, c.Session.UserId) hook.UserId = c.Session.UserId - hook.TeamId = c.Session.TeamId + hook.TeamId = c.TeamId var channel *model.Channel if result := <-cchan; result.Err != nil { @@ -73,7 +72,7 @@ func createIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) { } if !c.HasPermissionsToChannel(pchan, "createIncomingHook") { - if channel.Type != model.CHANNEL_OPEN || channel.TeamId != c.Session.TeamId { + if channel.Type != model.CHANNEL_OPEN || channel.TeamId != c.TeamId { c.LogAudit("fail - bad channel permissions") return } @@ -149,7 +148,7 @@ func getIncomingHooks(c *Context, w http.ResponseWriter, r *http.Request) { } } - if result := <-Srv.Store.Webhook().GetIncomingByTeam(c.Session.TeamId); result.Err != nil { + if result := <-Srv.Store.Webhook().GetIncomingByTeam(c.TeamId); result.Err != nil { c.Err = result.Err return } else { @@ -183,11 +182,11 @@ func createOutgoingHook(c *Context, w http.ResponseWriter, r *http.Request) { } hook.CreatorId = c.Session.UserId - hook.TeamId = c.Session.TeamId + hook.TeamId = c.TeamId if len(hook.ChannelId) != 0 { cchan := Srv.Store.Channel().Get(hook.ChannelId) - pchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, hook.ChannelId, c.Session.UserId) + pchan := Srv.Store.Channel().CheckPermissionsTo(c.TeamId, hook.ChannelId, c.Session.UserId) var channel *model.Channel if result := <-cchan; result.Err != nil { @@ -199,11 +198,14 @@ func createOutgoingHook(c *Context, w http.ResponseWriter, r *http.Request) { if channel.Type != model.CHANNEL_OPEN { c.LogAudit("fail - not open channel") + c.Err = model.NewLocAppError("createOutgoingHook", "api.webhook.create_outgoing.not_open.app_error", nil, "") + return } if !c.HasPermissionsToChannel(pchan, "createOutgoingHook") { - if channel.Type != model.CHANNEL_OPEN || channel.TeamId != c.Session.TeamId { + if channel.Type != model.CHANNEL_OPEN || channel.TeamId != c.TeamId { c.LogAudit("fail - bad channel permissions") + c.Err = model.NewLocAppError("createOutgoingHook", "api.webhook.create_outgoing.permissions.app_error", nil, "") return } } @@ -237,7 +239,7 @@ func getOutgoingHooks(c *Context, w http.ResponseWriter, r *http.Request) { } } - if result := <-Srv.Store.Webhook().GetOutgoingByTeam(c.Session.TeamId); result.Err != nil { + if result := <-Srv.Store.Webhook().GetOutgoingByTeam(c.TeamId); result.Err != nil { c.Err = result.Err return } else { @@ -292,7 +294,7 @@ func deleteOutgoingHook(c *Context, w http.ResponseWriter, r *http.Request) { } func regenOutgoingHookToken(c *Context, w http.ResponseWriter, r *http.Request) { - if !utils.Cfg.ServiceSettings.EnableIncomingWebhooks { + if !utils.Cfg.ServiceSettings.EnableOutgoingWebhooks { c.Err = model.NewLocAppError("regenOutgoingHookToken", "api.webhook.regen_outgoing_token.disabled.app_error", nil, "") c.Err.StatusCode = http.StatusNotImplemented return @@ -323,7 +325,7 @@ func regenOutgoingHookToken(c *Context, w http.ResponseWriter, r *http.Request) } else { hook = result.Data.(*model.OutgoingWebhook) - if c.Session.TeamId != hook.TeamId && c.Session.UserId != hook.CreatorId && !c.IsTeamAdmin() { + if c.TeamId != hook.TeamId && c.Session.UserId != hook.CreatorId && !c.IsTeamAdmin() { c.LogAudit("fail - inappropriate permissions") c.Err = model.NewLocAppError("regenOutgoingHookToken", "api.webhook.regen_outgoing_token.permissions.app_error", nil, "user_id="+c.Session.UserId) return @@ -398,7 +400,7 @@ func incomingWebhook(c *Context, w http.ResponseWriter, r *http.Request) { if len(channelName) != 0 { if channelName[0] == '@' { - if result := <-Srv.Store.User().GetByUsername(hook.TeamId, channelName[1:]); result.Err != nil { + if result := <-Srv.Store.User().GetByUsername(channelName[1:]); result.Err != nil { c.Err = model.NewLocAppError("incomingWebhook", "web.incoming_webhook.user.app_error", nil, "err="+result.Err.Message) return } else { @@ -426,7 +428,11 @@ func incomingWebhook(c *Context, w http.ResponseWriter, r *http.Request) { pchan := Srv.Store.Channel().CheckPermissionsTo(hook.TeamId, channel.Id, hook.UserId) // create a mock session - c.Session = model.Session{UserId: hook.UserId, TeamId: hook.TeamId, IsOAuth: false} + c.Session = model.Session{ + UserId: hook.UserId, + TeamMembers: []*model.TeamMember{{TeamId: hook.TeamId, UserId: hook.UserId}}, + IsOAuth: false, + } if !c.HasPermissionsToChannel(pchan, "createIncomingHook") && channel.Type != model.CHANNEL_OPEN { c.Err = model.NewLocAppError("incomingWebhook", "web.incoming_webhook.permissions.app_error", nil, "") diff --git a/api/webhook_test.go b/api/webhook_test.go index 4f85d178d..5198056cc 100644 --- a/api/webhook_test.go +++ b/api/webhook_test.go @@ -4,416 +4,599 @@ package api import ( + "fmt" "github.com/mattermost/platform/model" - "github.com/mattermost/platform/store" "github.com/mattermost/platform/utils" "testing" "time" ) func TestCreateIncomingHook(t *testing.T) { - Setup() + th := Setup().InitSystemAdmin() + Client := th.SystemAdminClient + user := th.SystemAdminUser + team := th.SystemAdminTeam + channel1 := th.CreateChannel(Client, team) + channel2 := th.CreatePrivateChannel(Client, team) + user2 := th.CreateUser(Client) + LinkUserToTeam(user2, team) + enableIncomingHooks := utils.Cfg.ServiceSettings.EnableIncomingWebhooks - enableOutgoingHooks := utils.Cfg.ServiceSettings.EnableOutgoingWebhooks + enableAdminOnlyHooks := utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations defer func() { utils.Cfg.ServiceSettings.EnableIncomingWebhooks = enableIncomingHooks - utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = enableOutgoingHooks + utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = enableAdminOnlyHooks }() utils.Cfg.ServiceSettings.EnableIncomingWebhooks = true - utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = true + *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = true - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) + hook := &model.IncomingWebhook{ChannelId: channel1.Id} - user := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user.Id)) + var rhook *model.IncomingWebhook + if result, err := Client.CreateIncomingWebhook(hook); err != nil { + t.Fatal(err) + } else { + rhook = result.Data.(*model.IncomingWebhook) + } - c := &Context{} - c.RequestId = model.NewId() - c.IpAddress = "cmd_line" - UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN) - Client.LoginByEmail(team.Name, user.Email, "pwd") + if hook.ChannelId != rhook.ChannelId { + t.Fatal("channel ids didn't match") + } - channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} - channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) + if rhook.UserId != user.Id { + t.Fatal("user ids didn't match") + } - channel2 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} - channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel) + if rhook.TeamId != team.Id { + t.Fatal("team ids didn't match") + } - hook := &model.IncomingWebhook{ChannelId: channel1.Id} + hook = &model.IncomingWebhook{ChannelId: "junk"} + if _, err := Client.CreateIncomingWebhook(hook); err == nil { + t.Fatal("should have failed - bad channel id") + } - if utils.Cfg.ServiceSettings.EnableIncomingWebhooks { - var rhook *model.IncomingWebhook - if result, err := Client.CreateIncomingWebhook(hook); err != nil { - t.Fatal(err) - } else { - rhook = result.Data.(*model.IncomingWebhook) + hook = &model.IncomingWebhook{ChannelId: channel2.Id, UserId: "123", TeamId: "456"} + if result, err := Client.CreateIncomingWebhook(hook); err != nil { + t.Fatal(err) + } else { + if result.Data.(*model.IncomingWebhook).UserId != user.Id { + t.Fatal("bad user id wasn't overwritten") } - - if hook.ChannelId != rhook.ChannelId { - t.Fatal("channel ids didn't match") + if result.Data.(*model.IncomingWebhook).TeamId != team.Id { + t.Fatal("bad team id wasn't overwritten") } + } - if rhook.UserId != user.Id { - t.Fatal("user ids didn't match") - } + Client.Logout() + Client.Must(Client.LoginById(user2.Id, user2.Password)) + Client.SetTeamId(team.Id) - if rhook.TeamId != team.Id { - t.Fatal("team ids didn't match") - } + hook = &model.IncomingWebhook{ChannelId: channel1.Id} - hook = &model.IncomingWebhook{ChannelId: "junk"} - if _, err := Client.CreateIncomingWebhook(hook); err == nil { - t.Fatal("should have failed - bad channel id") - } + if _, err := Client.CreateIncomingWebhook(hook); err == nil { + t.Fatal("should have failed - not system/team admin") + } - hook = &model.IncomingWebhook{ChannelId: channel2.Id, UserId: "123", TeamId: "456"} - if result, err := Client.CreateIncomingWebhook(hook); err != nil { - t.Fatal(err) - } else { - if result.Data.(*model.IncomingWebhook).UserId != user.Id { - t.Fatal("bad user id wasn't overwritten") - } - if result.Data.(*model.IncomingWebhook).TeamId != team.Id { - t.Fatal("bad team id wasn't overwritten") - } - } - } else { - if _, err := Client.CreateIncomingWebhook(hook); err == nil { - t.Fatal("should have errored - webhooks turned off") - } + *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = false + + if _, err := Client.CreateIncomingWebhook(hook); err != nil { + t.Fatal(err) + } + + hook = &model.IncomingWebhook{ChannelId: channel2.Id} + + if _, err := Client.CreateIncomingWebhook(hook); err == nil { + t.Fatal("should have failed - channel is private and not a member") + } + + utils.Cfg.ServiceSettings.EnableIncomingWebhooks = false + + if _, err := Client.CreateIncomingWebhook(hook); err == nil { + t.Fatal("should have errored - webhooks turned off") } } func TestListIncomingHooks(t *testing.T) { - Setup() + th := Setup().InitSystemAdmin() + Client := th.SystemAdminClient + team := th.SystemAdminTeam + channel1 := th.CreateChannel(Client, team) + user2 := th.CreateUser(Client) + LinkUserToTeam(user2, team) + enableIncomingHooks := utils.Cfg.ServiceSettings.EnableIncomingWebhooks - enableOutgoingHooks := utils.Cfg.ServiceSettings.EnableOutgoingWebhooks + enableAdminOnlyHooks := utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations defer func() { utils.Cfg.ServiceSettings.EnableIncomingWebhooks = enableIncomingHooks - utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = enableOutgoingHooks + utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = enableAdminOnlyHooks }() utils.Cfg.ServiceSettings.EnableIncomingWebhooks = true - utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = true + *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = true - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) + hook1 := &model.IncomingWebhook{ChannelId: channel1.Id} + hook1 = Client.Must(Client.CreateIncomingWebhook(hook1)).Data.(*model.IncomingWebhook) - user := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user.Id)) + hook2 := &model.IncomingWebhook{ChannelId: channel1.Id} + hook2 = Client.Must(Client.CreateIncomingWebhook(hook2)).Data.(*model.IncomingWebhook) - c := &Context{} - c.RequestId = model.NewId() - c.IpAddress = "cmd_line" - UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN) - Client.LoginByEmail(team.Name, user.Email, "pwd") + if result, err := Client.ListIncomingWebhooks(); err != nil { + t.Fatal(err) + } else { + hooks := result.Data.([]*model.IncomingWebhook) - channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} - channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) + if len(hooks) != 2 { + t.Fatal("incorrect number of hooks") + } + } - if utils.Cfg.ServiceSettings.EnableIncomingWebhooks { - hook1 := &model.IncomingWebhook{ChannelId: channel1.Id} - hook1 = Client.Must(Client.CreateIncomingWebhook(hook1)).Data.(*model.IncomingWebhook) + Client.Logout() + Client.Must(Client.LoginById(user2.Id, user2.Password)) + Client.SetTeamId(team.Id) - hook2 := &model.IncomingWebhook{ChannelId: channel1.Id} - hook2 = Client.Must(Client.CreateIncomingWebhook(hook2)).Data.(*model.IncomingWebhook) + if _, err := Client.ListIncomingWebhooks(); err == nil { + t.Fatal("should have errored - not system/team admin") + } - if result, err := Client.ListIncomingWebhooks(); err != nil { - t.Fatal(err) - } else { - hooks := result.Data.([]*model.IncomingWebhook) + *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = false - if len(hooks) != 2 { - t.Fatal("incorrect number of hooks") - } - } - } else { - if _, err := Client.ListIncomingWebhooks(); err == nil { - t.Fatal("should have errored - webhooks turned off") - } + if _, err := Client.ListIncomingWebhooks(); err != nil { + t.Fatal(err) + } + + utils.Cfg.ServiceSettings.EnableIncomingWebhooks = false + + if _, err := Client.ListIncomingWebhooks(); err == nil { + t.Fatal("should have errored - webhooks turned off") } } func TestDeleteIncomingHook(t *testing.T) { - Setup() + th := Setup().InitSystemAdmin() + Client := th.SystemAdminClient + team := th.SystemAdminTeam + channel1 := th.CreateChannel(Client, team) + user2 := th.CreateUser(Client) + LinkUserToTeam(user2, team) + enableIncomingHooks := utils.Cfg.ServiceSettings.EnableIncomingWebhooks - enableOutgoingHooks := utils.Cfg.ServiceSettings.EnableOutgoingWebhooks + enableAdminOnlyHooks := utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations defer func() { utils.Cfg.ServiceSettings.EnableIncomingWebhooks = enableIncomingHooks - utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = enableOutgoingHooks + utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = enableAdminOnlyHooks }() utils.Cfg.ServiceSettings.EnableIncomingWebhooks = true - utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = true + *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = true - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) + hook := &model.IncomingWebhook{ChannelId: channel1.Id} + hook = Client.Must(Client.CreateIncomingWebhook(hook)).Data.(*model.IncomingWebhook) - user := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user.Id)) + if _, err := Client.DeleteIncomingWebhook(hook.Id); err != nil { + t.Fatal(err) + } - c := &Context{} - c.RequestId = model.NewId() - c.IpAddress = "cmd_line" - UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN) - Client.LoginByEmail(team.Name, user.Email, "pwd") + if _, err := Client.DeleteIncomingWebhook("junk"); err == nil { + t.Fatal("should have failed - bad id") + } - channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} - channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) + if _, err := Client.DeleteIncomingWebhook(""); err == nil { + t.Fatal("should have failed - empty id") + } - if utils.Cfg.ServiceSettings.EnableIncomingWebhooks { - hook := &model.IncomingWebhook{ChannelId: channel1.Id} - hook = Client.Must(Client.CreateIncomingWebhook(hook)).Data.(*model.IncomingWebhook) + hooks := Client.Must(Client.ListIncomingWebhooks()).Data.([]*model.IncomingWebhook) + if len(hooks) != 0 { + t.Fatal("delete didn't work properly") + } - data := make(map[string]string) - data["id"] = hook.Id + hook = &model.IncomingWebhook{ChannelId: channel1.Id} + hook = Client.Must(Client.CreateIncomingWebhook(hook)).Data.(*model.IncomingWebhook) - if _, err := Client.DeleteIncomingWebhook(data); err != nil { - t.Fatal(err) - } + Client.Logout() + Client.Must(Client.LoginById(user2.Id, user2.Password)) + Client.SetTeamId(team.Id) - hooks := Client.Must(Client.ListIncomingWebhooks()).Data.([]*model.IncomingWebhook) - if len(hooks) != 0 { - t.Fatal("delete didn't work properly") - } - } else { - data := make(map[string]string) - data["id"] = "123" + if _, err := Client.DeleteIncomingWebhook(hook.Id); err == nil { + t.Fatal("should have failed - not system/team admin") + } - if _, err := Client.DeleteIncomingWebhook(data); err == nil { - t.Fatal("should have errored - webhooks turned off") - } + *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = false + + if _, err := Client.DeleteIncomingWebhook(hook.Id); err == nil { + t.Fatal("should have failed - not creator or team admin") + } + + hook = &model.IncomingWebhook{ChannelId: channel1.Id} + hook = Client.Must(Client.CreateIncomingWebhook(hook)).Data.(*model.IncomingWebhook) + + if _, err := Client.DeleteIncomingWebhook(hook.Id); err != nil { + t.Fatal(err) + } + + utils.Cfg.ServiceSettings.EnableIncomingWebhooks = false + + if _, err := Client.DeleteIncomingWebhook(hook.Id); err == nil { + t.Fatal("should have errored - webhooks turned off") } } func TestCreateOutgoingHook(t *testing.T) { - Setup() - enableIncomingHooks := utils.Cfg.ServiceSettings.EnableIncomingWebhooks + th := Setup().InitSystemAdmin() + Client := th.SystemAdminClient + user := th.SystemAdminUser + team := th.SystemAdminTeam + team2 := th.CreateTeam(Client) + channel1 := th.CreateChannel(Client, team) + channel2 := th.CreatePrivateChannel(Client, team) + user2 := th.CreateUser(Client) + LinkUserToTeam(user2, team) + user3 := th.CreateUser(Client) + LinkUserToTeam(user3, team2) + enableOutgoingHooks := utils.Cfg.ServiceSettings.EnableOutgoingWebhooks + enableAdminOnlyHooks := utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations defer func() { - utils.Cfg.ServiceSettings.EnableIncomingWebhooks = enableIncomingHooks utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = enableOutgoingHooks + utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = enableAdminOnlyHooks }() - utils.Cfg.ServiceSettings.EnableIncomingWebhooks = true utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = true + *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = true - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) + hook := &model.OutgoingWebhook{ChannelId: channel1.Id, CallbackURLs: []string{"http://nowhere.com"}} - user := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user.Id)) + var rhook *model.OutgoingWebhook + if result, err := Client.CreateOutgoingWebhook(hook); err != nil { + t.Fatal(err) + } else { + rhook = result.Data.(*model.OutgoingWebhook) + } - c := &Context{} - c.RequestId = model.NewId() - c.IpAddress = "cmd_line" - UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN) - Client.LoginByEmail(team.Name, user.Email, "pwd") + if hook.ChannelId != rhook.ChannelId { + t.Fatal("channel ids didn't match") + } - channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} - channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) + if rhook.CreatorId != user.Id { + t.Fatal("user ids didn't match") + } - channel2 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} - channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel) + if rhook.TeamId != team.Id { + t.Fatal("team ids didn't match") + } - hook := &model.OutgoingWebhook{ChannelId: channel1.Id, CallbackURLs: []string{"http://nowhere.com"}} + hook = &model.OutgoingWebhook{ChannelId: "junk", CallbackURLs: []string{"http://nowhere.com"}} + if _, err := Client.CreateOutgoingWebhook(hook); err == nil { + t.Fatal("should have failed - bad channel id") + } - if utils.Cfg.ServiceSettings.EnableOutgoingWebhooks { - var rhook *model.OutgoingWebhook - if result, err := Client.CreateOutgoingWebhook(hook); err != nil { - t.Fatal(err) - } else { - rhook = result.Data.(*model.OutgoingWebhook) + hook = &model.OutgoingWebhook{ChannelId: channel1.Id, CreatorId: "123", TeamId: "456", CallbackURLs: []string{"http://nowhere.com"}} + if result, err := Client.CreateOutgoingWebhook(hook); err != nil { + t.Fatal(err) + } else { + if result.Data.(*model.OutgoingWebhook).CreatorId != user.Id { + t.Fatal("bad user id wasn't overwritten") } - - if hook.ChannelId != rhook.ChannelId { - t.Fatal("channel ids didn't match") + if result.Data.(*model.OutgoingWebhook).TeamId != team.Id { + t.Fatal("bad team id wasn't overwritten") } + } - if rhook.CreatorId != user.Id { - t.Fatal("user ids didn't match") - } + hook = &model.OutgoingWebhook{ChannelId: channel2.Id, CallbackURLs: []string{"http://nowhere.com"}} + if _, err := Client.CreateOutgoingWebhook(hook); err == nil { + t.Fatal("should have failed - private channel") + } - if rhook.TeamId != team.Id { - t.Fatal("team ids didn't match") - } + hook = &model.OutgoingWebhook{CallbackURLs: []string{"http://nowhere.com"}} + if _, err := Client.CreateOutgoingWebhook(hook); err == nil { + t.Fatal("should have failed - blank channel and trigger words") + } - hook = &model.OutgoingWebhook{ChannelId: "junk", CallbackURLs: []string{"http://nowhere.com"}} - if _, err := Client.CreateOutgoingWebhook(hook); err == nil { - t.Fatal("should have failed - bad channel id") - } + Client.Logout() + Client.Must(Client.LoginById(user2.Id, user2.Password)) + Client.SetTeamId(team.Id) - hook = &model.OutgoingWebhook{ChannelId: channel2.Id, CreatorId: "123", TeamId: "456", CallbackURLs: []string{"http://nowhere.com"}} - if result, err := Client.CreateOutgoingWebhook(hook); err != nil { - t.Fatal(err) - } else { - if result.Data.(*model.OutgoingWebhook).CreatorId != user.Id { - t.Fatal("bad user id wasn't overwritten") - } - if result.Data.(*model.OutgoingWebhook).TeamId != team.Id { - t.Fatal("bad team id wasn't overwritten") - } - } - } else { - if _, err := Client.CreateOutgoingWebhook(hook); err == nil { - t.Fatal("should have errored - webhooks turned off") - } + hook = &model.OutgoingWebhook{ChannelId: channel1.Id, CallbackURLs: []string{"http://nowhere.com"}} + if _, err := Client.CreateOutgoingWebhook(hook); err == nil { + t.Fatal("should have failed - not system/team admin") + } + + *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = false + + if _, err := Client.CreateOutgoingWebhook(hook); err != nil { + t.Fatal(err) + } + + Client.Logout() + Client.Must(Client.LoginById(user3.Id, user3.Password)) + Client.SetTeamId(team2.Id) + + if _, err := Client.CreateOutgoingWebhook(hook); err == nil { + t.Fatal("should have failed - wrong team") + } + + utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = false + + if _, err := Client.CreateOutgoingWebhook(hook); err == nil { + t.Fatal("should have errored - webhooks turned off") } } func TestListOutgoingHooks(t *testing.T) { - Setup() - enableIncomingHooks := utils.Cfg.ServiceSettings.EnableIncomingWebhooks + th := Setup().InitSystemAdmin() + Client := th.SystemAdminClient + team := th.SystemAdminTeam + channel1 := th.CreateChannel(Client, team) + user2 := th.CreateUser(Client) + LinkUserToTeam(user2, team) + enableOutgoingHooks := utils.Cfg.ServiceSettings.EnableOutgoingWebhooks + enableAdminOnlyHooks := utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations defer func() { - utils.Cfg.ServiceSettings.EnableIncomingWebhooks = enableIncomingHooks utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = enableOutgoingHooks + utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = enableAdminOnlyHooks }() - utils.Cfg.ServiceSettings.EnableIncomingWebhooks = true utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = true + *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = true - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) + hook1 := &model.OutgoingWebhook{ChannelId: channel1.Id, CallbackURLs: []string{"http://nowhere.com"}} + hook1 = Client.Must(Client.CreateOutgoingWebhook(hook1)).Data.(*model.OutgoingWebhook) - user := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user.Id)) + hook2 := &model.OutgoingWebhook{TriggerWords: []string{"trigger"}, CallbackURLs: []string{"http://nowhere.com"}} + hook2 = Client.Must(Client.CreateOutgoingWebhook(hook2)).Data.(*model.OutgoingWebhook) - c := &Context{} - c.RequestId = model.NewId() - c.IpAddress = "cmd_line" - UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN) - Client.LoginByEmail(team.Name, user.Email, "pwd") + if result, err := Client.ListOutgoingWebhooks(); err != nil { + t.Fatal(err) + } else { + hooks := result.Data.([]*model.OutgoingWebhook) - channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} - channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) + if len(hooks) != 2 { + t.Fatal("incorrect number of hooks") + } + } - if utils.Cfg.ServiceSettings.EnableOutgoingWebhooks { - hook1 := &model.OutgoingWebhook{ChannelId: channel1.Id, CallbackURLs: []string{"http://nowhere.com"}} - hook1 = Client.Must(Client.CreateOutgoingWebhook(hook1)).Data.(*model.OutgoingWebhook) + Client.Logout() + Client.Must(Client.LoginById(user2.Id, user2.Password)) + Client.SetTeamId(team.Id) - hook2 := &model.OutgoingWebhook{TriggerWords: []string{"trigger"}, CallbackURLs: []string{"http://nowhere.com"}} - hook2 = Client.Must(Client.CreateOutgoingWebhook(hook2)).Data.(*model.OutgoingWebhook) + if _, err := Client.ListOutgoingWebhooks(); err == nil { + t.Fatal("should have failed - not system/team admin") + } - if result, err := Client.ListOutgoingWebhooks(); err != nil { - t.Fatal(err) - } else { - hooks := result.Data.([]*model.OutgoingWebhook) + *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = false - if len(hooks) != 2 { - t.Fatal("incorrect number of hooks") - } - } - } else { - if _, err := Client.ListOutgoingWebhooks(); err == nil { - t.Fatal("should have errored - webhooks turned off") - } + if _, err := Client.ListOutgoingWebhooks(); err != nil { + t.Fatal(err) + } + + utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = false + + if _, err := Client.ListOutgoingWebhooks(); err == nil { + t.Fatal("should have errored - webhooks turned off") } } func TestDeleteOutgoingHook(t *testing.T) { - Setup() - enableIncomingHooks := utils.Cfg.ServiceSettings.EnableIncomingWebhooks + th := Setup().InitSystemAdmin() + Client := th.SystemAdminClient + team := th.SystemAdminTeam + channel1 := th.CreateChannel(Client, team) + user2 := th.CreateUser(Client) + LinkUserToTeam(user2, team) + enableOutgoingHooks := utils.Cfg.ServiceSettings.EnableOutgoingWebhooks + enableAdminOnlyHooks := utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations defer func() { - utils.Cfg.ServiceSettings.EnableIncomingWebhooks = enableIncomingHooks utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = enableOutgoingHooks + utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = enableAdminOnlyHooks }() - utils.Cfg.ServiceSettings.EnableIncomingWebhooks = true utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = true + *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = true - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) + hook := &model.OutgoingWebhook{ChannelId: channel1.Id, CallbackURLs: []string{"http://nowhere.com"}} + hook = Client.Must(Client.CreateOutgoingWebhook(hook)).Data.(*model.OutgoingWebhook) - user := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user.Id)) + if _, err := Client.DeleteOutgoingWebhook("junk"); err == nil { + t.Fatal("should have failed - bad hook id") + } - c := &Context{} - c.RequestId = model.NewId() - c.IpAddress = "cmd_line" - UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN) - Client.LoginByEmail(team.Name, user.Email, "pwd") + if _, err := Client.DeleteOutgoingWebhook(""); err == nil { + t.Fatal("should have failed - empty hook id") + } - channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} - channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) + if _, err := Client.DeleteOutgoingWebhook(hook.Id); err != nil { + t.Fatal(err) + } - if utils.Cfg.ServiceSettings.EnableOutgoingWebhooks { - hook := &model.OutgoingWebhook{ChannelId: channel1.Id, CallbackURLs: []string{"http://nowhere.com"}} - hook = Client.Must(Client.CreateOutgoingWebhook(hook)).Data.(*model.OutgoingWebhook) + hooks := Client.Must(Client.ListOutgoingWebhooks()).Data.([]*model.OutgoingWebhook) + if len(hooks) != 0 { + t.Fatal("delete didn't work properly") + } - data := make(map[string]string) - data["id"] = hook.Id + hook = &model.OutgoingWebhook{ChannelId: channel1.Id, CallbackURLs: []string{"http://nowhere.com"}} + hook = Client.Must(Client.CreateOutgoingWebhook(hook)).Data.(*model.OutgoingWebhook) - if _, err := Client.DeleteOutgoingWebhook(data); err != nil { - t.Fatal(err) - } + Client.Logout() + Client.Must(Client.LoginById(user2.Id, user2.Password)) + Client.SetTeamId(team.Id) - hooks := Client.Must(Client.ListOutgoingWebhooks()).Data.([]*model.OutgoingWebhook) - if len(hooks) != 0 { - t.Fatal("delete didn't work properly") - } - } else { - data := make(map[string]string) - data["id"] = "123" + if _, err := Client.DeleteOutgoingWebhook(hook.Id); err == nil { + t.Fatal("should have failed - not system/team admin") + } - if _, err := Client.DeleteOutgoingWebhook(data); err == nil { - t.Fatal("should have errored - webhooks turned off") - } + *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = false + + if _, err := Client.DeleteOutgoingWebhook(hook.Id); err == nil { + t.Fatal("should have failed - not creator or team admin") + } + + hook = &model.OutgoingWebhook{ChannelId: channel1.Id, CallbackURLs: []string{"http://nowhere.com"}} + hook = Client.Must(Client.CreateOutgoingWebhook(hook)).Data.(*model.OutgoingWebhook) + + if _, err := Client.DeleteOutgoingWebhook(hook.Id); err != nil { + t.Fatal(err) + } + + utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = false + + if _, err := Client.DeleteOutgoingWebhook(hook.Id); err == nil { + t.Fatal("should have errored - webhooks turned off") } } func TestRegenOutgoingHookToken(t *testing.T) { - Setup() - enableIncomingHooks := utils.Cfg.ServiceSettings.EnableIncomingWebhooks + th := Setup().InitSystemAdmin() + Client := th.SystemAdminClient + team := th.SystemAdminTeam + team2 := th.CreateTeam(Client) + channel1 := th.CreateChannel(Client, team) + user2 := th.CreateUser(Client) + LinkUserToTeam(user2, team) + user3 := th.CreateUser(Client) + LinkUserToTeam(user3, team2) + enableOutgoingHooks := utils.Cfg.ServiceSettings.EnableOutgoingWebhooks + enableAdminOnlyHooks := utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations defer func() { - utils.Cfg.ServiceSettings.EnableIncomingWebhooks = enableIncomingHooks utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = enableOutgoingHooks + utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = enableAdminOnlyHooks }() - utils.Cfg.ServiceSettings.EnableIncomingWebhooks = true utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = true + *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = true - team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) + hook := &model.OutgoingWebhook{ChannelId: channel1.Id, CallbackURLs: []string{"http://nowhere.com"}} + hook = Client.Must(Client.CreateOutgoingWebhook(hook)).Data.(*model.OutgoingWebhook) - user := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"} - user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) - store.Must(Srv.Store.User().VerifyEmail(user.Id)) + if _, err := Client.RegenOutgoingWebhookToken("junk"); err == nil { + t.Fatal("should have failed - bad id") + } - c := &Context{} - c.RequestId = model.NewId() - c.IpAddress = "cmd_line" - UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN) - Client.LoginByEmail(team.Name, user.Email, "pwd") + if _, err := Client.RegenOutgoingWebhookToken(""); err == nil { + t.Fatal("should have failed - empty id") + } - channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} - channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) + if result, err := Client.RegenOutgoingWebhookToken(hook.Id); err != nil { + t.Fatal(err) + } else { + if result.Data.(*model.OutgoingWebhook).Token == hook.Token { + t.Fatal("regen didn't work properly") + } + } - if utils.Cfg.ServiceSettings.EnableOutgoingWebhooks { - hook := &model.OutgoingWebhook{ChannelId: channel1.Id, CallbackURLs: []string{"http://nowhere.com"}} - hook = Client.Must(Client.CreateOutgoingWebhook(hook)).Data.(*model.OutgoingWebhook) + Client.Logout() + Client.Must(Client.LoginById(user2.Id, user2.Password)) + Client.SetTeamId(team.Id) - data := make(map[string]string) - data["id"] = hook.Id + if _, err := Client.RegenOutgoingWebhookToken(hook.Id); err == nil { + t.Fatal("should have failed - not system/team admin") + } - if result, err := Client.RegenOutgoingWebhookToken(data); err != nil { - t.Fatal(err) - } else { - if result.Data.(*model.OutgoingWebhook).Token == hook.Token { - t.Fatal("regen didn't work properly") - } - } + *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = false - } else { - data := make(map[string]string) - data["id"] = "123" + hook = &model.OutgoingWebhook{ChannelId: channel1.Id, CallbackURLs: []string{"http://nowhere.com"}} + hook = Client.Must(Client.CreateOutgoingWebhook(hook)).Data.(*model.OutgoingWebhook) - if _, err := Client.RegenOutgoingWebhookToken(data); err == nil { - t.Fatal("should have errored - webhooks turned off") - } + if _, err := Client.RegenOutgoingWebhookToken(hook.Id); err != nil { + t.Fatal(err) + } + + Client.Logout() + Client.Must(Client.LoginById(user3.Id, user3.Password)) + Client.SetTeamId(team2.Id) + + if _, err := Client.RegenOutgoingWebhookToken(hook.Id); err == nil { + t.Fatal("should have failed - wrong team") + } + + utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = false + + if _, err := Client.RegenOutgoingWebhookToken(hook.Id); err == nil { + t.Fatal("should have errored - webhooks turned off") + } +} +func TestIncomingWebhooks(t *testing.T) { + th := Setup().InitSystemAdmin() + Client := th.SystemAdminClient + team := th.SystemAdminTeam + channel1 := th.CreateChannel(Client, team) + user2 := th.CreateUser(Client) + LinkUserToTeam(user2, team) + + enableIncomingHooks := utils.Cfg.ServiceSettings.EnableIncomingWebhooks + defer func() { + utils.Cfg.ServiceSettings.EnableIncomingWebhooks = enableIncomingHooks + }() + utils.Cfg.ServiceSettings.EnableIncomingWebhooks = true + + hook := &model.IncomingWebhook{ChannelId: channel1.Id} + hook = Client.Must(Client.CreateIncomingWebhook(hook)).Data.(*model.IncomingWebhook) + + url := "/hooks/" + hook.Id + + if _, err := Client.DoPost(url, "{\"text\":\"this is a test\"}", "application/json"); err != nil { + t.Fatal(err) + } + + if _, err := Client.DoPost(url, fmt.Sprintf("{\"text\":\"this is a test\", \"channel\":\"%s\"}", channel1.Name), "application/json"); err != nil { + t.Fatal(err) + } + + if _, err := Client.DoPost(url, fmt.Sprintf("{\"text\":\"this is a test\", \"channel\":\"#%s\"}", channel1.Name), "application/json"); err != nil { + t.Fatal(err) + } + + Client.Must(Client.CreateDirectChannel(user2.Id)) + + if _, err := Client.DoPost(url, fmt.Sprintf("{\"text\":\"this is a test\", \"channel\":\"@%s\"}", user2.Username), "application/json"); err != nil { + t.Fatal(err) + } + + if _, err := Client.DoPost(url, "payload={\"text\":\"this is a test\"}", "application/x-www-form-urlencoded"); err != nil { + t.Fatal(err) + } + + attachmentPayload := `{ + "text": "this is a test", + "attachments": [ + { + "fallback": "Required plain-text summary of the attachment.", + + "color": "#36a64f", + + "pretext": "Optional text that appears above the attachment block", + + "author_name": "Bobby Tables", + "author_link": "http://flickr.com/bobby/", + "author_icon": "http://flickr.com/icons/bobby.jpg", + + "title": "Slack API Documentation", + "title_link": "https://api.slack.com/", + + "text": "Optional text that appears within the attachment", + + "fields": [ + { + "title": "Priority", + "value": "High", + "short": false + } + ], + + "image_url": "http://my-website.com/path/to/image.jpg", + "thumb_url": "http://example.com/path/to/thumb.png" + } + ] + }` + + if _, err := Client.DoPost(url, attachmentPayload, "application/json"); err != nil { + t.Fatal(err) + } + + if _, err := Client.DoPost(url, "{\"text\":\"\"}", "application/json"); err == nil { + t.Fatal("should have failed - no text") + } + + utils.Cfg.ServiceSettings.EnableIncomingWebhooks = false + + if _, err := Client.DoPost(url, "{\"text\":\"this is a test\"}", "application/json"); err == nil { + t.Fatal("should have failed - webhooks turned off") } } -- cgit v1.2.3-1-g7c22