From d3a0e561c265e32a305cd5c6ed5e80f461d9f4eb Mon Sep 17 00:00:00 2001 From: Harrison Healey Date: Thu, 8 Feb 2018 10:45:25 -0500 Subject: ICU-669 Ensured all URLs returned from OpenGraph are absolute --- app/post.go | 59 ++++++++++++++++++++++++++++++++++++++--- app/post_test.go | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+), 4 deletions(-) diff --git a/app/post.go b/app/post.go index 005624605..1e170d363 100644 --- a/app/post.go +++ b/app/post.go @@ -12,6 +12,7 @@ import ( "encoding/json" "fmt" "net/http" + "net/url" "regexp" "strings" @@ -734,18 +735,68 @@ func (a *App) GetFileInfosForPost(postId string, readFromMaster bool) ([]*model. return infos, nil } -func (a *App) GetOpenGraphMetadata(url string) *opengraph.OpenGraph { +func (a *App) GetOpenGraphMetadata(requestURL string) *opengraph.OpenGraph { og := opengraph.NewOpenGraph() - res, err := a.HTTPClient(false).Get(url) + res, err := a.HTTPClient(false).Get(requestURL) if err != nil { - l4g.Error("GetOpenGraphMetadata request failed for url=%v with err=%v", url, err.Error()) + l4g.Error("GetOpenGraphMetadata request failed for url=%v with err=%v", requestURL, err.Error()) return og } defer consumeAndClose(res) if err := og.ProcessHTML(res.Body); err != nil { - l4g.Error("GetOpenGraphMetadata processing failed for url=%v with err=%v", url, err.Error()) + l4g.Error("GetOpenGraphMetadata processing failed for url=%v with err=%v", requestURL, err.Error()) + } + + og = makeOpenGraphURLsAbsolute(og, requestURL) + + return og +} + +func makeOpenGraphURLsAbsolute(og *opengraph.OpenGraph, requestURL string) *opengraph.OpenGraph { + parsedRequestURL, err := url.Parse(requestURL) + if err != nil { + l4g.Warn("makeOpenGraphURLsAbsolute failed to parse url=%v", requestURL) + return og + } + + makeURLAbsolute := func(resultURL string) string { + if resultURL == "" { + return resultURL + } + + parsedResultURL, err := url.Parse(resultURL) + if err != nil { + l4g.Warn("makeOpenGraphURLsAbsolute failed to parse result url=%v", resultURL) + return resultURL + } + + if parsedResultURL.IsAbs() { + return resultURL + } + + parsedResultURL.Scheme = parsedRequestURL.Scheme + parsedResultURL.Host = parsedRequestURL.Host + + return parsedResultURL.String() + } + + og.URL = makeURLAbsolute(og.URL) + + for _, image := range og.Images { + image.URL = makeURLAbsolute(image.URL) + image.SecureURL = makeURLAbsolute(image.SecureURL) + } + + for _, audio := range og.Audios { + audio.URL = makeURLAbsolute(audio.URL) + audio.SecureURL = makeURLAbsolute(audio.SecureURL) + } + + for _, video := range og.Videos { + video.URL = makeURLAbsolute(video.URL) + video.SecureURL = makeURLAbsolute(video.SecureURL) } return og diff --git a/app/post_test.go b/app/post_test.go index 3f3783265..987879a72 100644 --- a/app/post_test.go +++ b/app/post_test.go @@ -8,9 +8,11 @@ import ( "fmt" "net/http" "net/http/httptest" + "strings" "testing" "time" + "github.com/dyatlov/go-opengraph/opengraph" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -250,6 +252,84 @@ func TestImageProxy(t *testing.T) { } } +func TestMakeOpenGraphURLsAbsolute(t *testing.T) { + for name, tc := range map[string]struct { + HTML string + RequestURL string + URL string + ImageURL string + }{ + "absolute URLs": { + HTML: ` + + + + + + `, + RequestURL: "https://example.com", + URL: "https://example.com/apps/mattermost", + ImageURL: "https://images.example.com/image.png", + }, + "relative URLs": { + HTML: ` + + + + + + `, + RequestURL: "http://example.com", + URL: "http://example.com/apps/mattermost", + ImageURL: "http://example.com/image.png", + }, + "relative URLs with HTTPS": { + HTML: ` + + + + + + `, + RequestURL: "https://example.com", + URL: "https://example.com/apps/mattermost", + ImageURL: "https://example.com/image.png", + }, + "missing image URL": { + HTML: ` + + + + + `, + RequestURL: "http://example.com", + URL: "http://example.com/apps/mattermost", + ImageURL: "", + }, + } { + t.Run(name, func(t *testing.T) { + og := opengraph.NewOpenGraph() + if err := og.ProcessHTML(strings.NewReader(tc.HTML)); err != nil { + t.Fatal(err) + } + + og = makeOpenGraphURLsAbsolute(og, tc.RequestURL) + + if og.URL != tc.URL { + t.Fatalf("incorrect url, expected %v, got %v", tc.URL, og.URL) + } + + if len(og.Images) > 0 { + if og.Images[0].URL != tc.ImageURL { + t.Fatalf("incorrect image url, expected %v, got %v", tc.ImageURL, og.Images[0].URL) + } + } else if tc.ImageURL != "" { + t.Fatal("missing image url, expected %v, got nothing", tc.ImageURL) + } + }) + } +} + var imageProxyBenchmarkSink *model.Post func BenchmarkPostWithProxyRemovedFromImageURLs(b *testing.B) { -- cgit v1.2.3-1-g7c22 From e2b5f9217f55074e4a64c90f4121803cd68f0b97 Mon Sep 17 00:00:00 2001 From: Harrison Healey Date: Fri, 9 Feb 2018 10:05:23 -0500 Subject: ICU-669 Handle relative links better --- app/post.go | 13 ++++--------- app/post_test.go | 18 +++++++++++++++--- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/app/post.go b/app/post.go index 1e170d363..f8a371fc0 100644 --- a/app/post.go +++ b/app/post.go @@ -749,16 +749,16 @@ func (a *App) GetOpenGraphMetadata(requestURL string) *opengraph.OpenGraph { l4g.Error("GetOpenGraphMetadata processing failed for url=%v with err=%v", requestURL, err.Error()) } - og = makeOpenGraphURLsAbsolute(og, requestURL) + makeOpenGraphURLsAbsolute(og, requestURL) return og } -func makeOpenGraphURLsAbsolute(og *opengraph.OpenGraph, requestURL string) *opengraph.OpenGraph { +func makeOpenGraphURLsAbsolute(og *opengraph.OpenGraph, requestURL string) { parsedRequestURL, err := url.Parse(requestURL) if err != nil { l4g.Warn("makeOpenGraphURLsAbsolute failed to parse url=%v", requestURL) - return og + return } makeURLAbsolute := func(resultURL string) string { @@ -776,10 +776,7 @@ func makeOpenGraphURLsAbsolute(og *opengraph.OpenGraph, requestURL string) *open return resultURL } - parsedResultURL.Scheme = parsedRequestURL.Scheme - parsedResultURL.Host = parsedRequestURL.Host - - return parsedResultURL.String() + return parsedRequestURL.ResolveReference(parsedResultURL).String() } og.URL = makeURLAbsolute(og.URL) @@ -798,8 +795,6 @@ func makeOpenGraphURLsAbsolute(og *opengraph.OpenGraph, requestURL string) *open video.URL = makeURLAbsolute(video.URL) video.SecureURL = makeURLAbsolute(video.SecureURL) } - - return og } func (a *App) DoPostAction(postId string, actionId string, userId string) *model.AppError { diff --git a/app/post_test.go b/app/post_test.go index 987879a72..62098c865 100644 --- a/app/post_test.go +++ b/app/post_test.go @@ -271,7 +271,7 @@ func TestMakeOpenGraphURLsAbsolute(t *testing.T) { URL: "https://example.com/apps/mattermost", ImageURL: "https://images.example.com/image.png", }, - "relative URLs": { + "URLs starting with /": { HTML: ` @@ -283,7 +283,7 @@ func TestMakeOpenGraphURLsAbsolute(t *testing.T) { URL: "http://example.com/apps/mattermost", ImageURL: "http://example.com/image.png", }, - "relative URLs with HTTPS": { + "HTTPS URLs starting with /": { HTML: ` @@ -306,6 +306,18 @@ func TestMakeOpenGraphURLsAbsolute(t *testing.T) { URL: "http://example.com/apps/mattermost", ImageURL: "", }, + "relative URLs": { + HTML: ` + + + + + + `, + RequestURL: "http://example.com/content/index.html", + URL: "http://example.com/content/index.html", + ImageURL: "http://example.com/resources/image.png", + }, } { t.Run(name, func(t *testing.T) { og := opengraph.NewOpenGraph() @@ -313,7 +325,7 @@ func TestMakeOpenGraphURLsAbsolute(t *testing.T) { t.Fatal(err) } - og = makeOpenGraphURLsAbsolute(og, tc.RequestURL) + makeOpenGraphURLsAbsolute(og, tc.RequestURL) if og.URL != tc.URL { t.Fatalf("incorrect url, expected %v, got %v", tc.URL, og.URL) -- cgit v1.2.3-1-g7c22 From 87fb19b8279c86c72ffec623e55b80ce35b7d64f Mon Sep 17 00:00:00 2001 From: Harrison Healey Date: Mon, 12 Feb 2018 08:58:38 -0500 Subject: Fixed typo in unit test --- app/post_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/post_test.go b/app/post_test.go index 62098c865..f5a5a23cb 100644 --- a/app/post_test.go +++ b/app/post_test.go @@ -336,7 +336,7 @@ func TestMakeOpenGraphURLsAbsolute(t *testing.T) { t.Fatalf("incorrect image url, expected %v, got %v", tc.ImageURL, og.Images[0].URL) } } else if tc.ImageURL != "" { - t.Fatal("missing image url, expected %v, got nothing", tc.ImageURL) + t.Fatalf("missing image url, expected %v, got nothing", tc.ImageURL) } }) } -- cgit v1.2.3-1-g7c22 From c209e4457457edc042f063390c9a222a694f3a6d Mon Sep 17 00:00:00 2001 From: Derrick Anderson Date: Mon, 12 Feb 2018 16:01:02 -0500 Subject: revert master changes --- Makefile | 4 +- api/api.go | 6 +- api/apitestlib.go | 11 +- api/channel_test.go | 245 +++++++++++++++++++-- api/context.go | 6 +- api/file.go | 4 +- api/license.go | 7 +- api/license_test.go | 8 +- api/post_test.go | 42 +++- api/team_test.go | 42 +++- api/user.go | 4 +- api/user_test.go | 15 +- api/webhook_test.go | 38 +++- api4/apitestlib.go | 14 +- api4/channel_test.go | 148 +++++++++++-- api4/file.go | 4 +- api4/oauth.go | 24 +- api4/oauth_test.go | 62 ++++++ api4/post_test.go | 43 +++- api4/system.go | 6 +- api4/team_test.go | 71 +++++- api4/user_test.go | 53 ++++- app/admin.go | 3 +- app/app.go | 56 ++--- app/app_test.go | 3 +- app/apptestlib.go | 11 +- app/channel.go | 30 +-- app/channel_test.go | 4 +- app/config.go | 98 +-------- app/config_test.go | 9 - app/diagnostics.go | 1 - app/email.go | 3 +- app/file.go | 3 +- app/license.go | 107 +-------- app/license_test.go | 75 +------ app/plugin.go | 1 - app/role.go | 6 +- app/server.go | 12 +- app/server_test.go | 50 ----- app/session_test.go | 22 +- cmd/platform/channel.go | 17 +- cmd/platform/channel_test.go | 27 --- cmd/platform/jobserver.go | 2 +- cmd/platform/server.go | 19 +- cmd/platform/server_test.go | 72 ------ cmd/platform/test.go | 12 +- i18n/en.json | 36 ++- jobs/jobs_watcher.go | 6 +- jobs/server.go | 33 +++ jobs/server_test.go | 39 ++++ model/channel_member_history.go | 5 +- model/client4.go | 4 +- model/client4_test.go | 58 ----- model/config.go | 21 -- model/config_test.go | 52 +---- model/license.go | 19 -- model/message_export.go | 1 - model/post.go | 15 +- model/system.go | 23 +- store/sqlstore/channel_member_history_store.go | 12 +- store/sqlstore/compliance_store.go | 3 +- store/sqlstore/post_store.go | 15 +- store/sqlstore/upgrade.go | 9 - store/storetest/channel_member_history_store.go | 16 -- store/storetest/compliance_store.go | 9 +- store/storetest/post_store.go | 23 +- templates/globalrelay_compliance_export.html | 91 -------- .../globalrelay_compliance_export_message.html | 8 - ...balrelay_compliance_export_participant_row.html | 10 - utils/api.go | 25 +-- utils/api_test.go | 49 ----- utils/authorization.go | 20 +- utils/config.go | 31 +-- utils/config_test.go | 2 +- utils/file_backend.go | 4 +- utils/file_backend_test.go | 2 +- utils/html.go | 8 +- utils/inbucket.go | 52 +---- utils/license.go | 135 +++++++++++- utils/license_test.go | 67 ++++++ utils/mail.go | 62 +----- utils/mail_test.go | 89 +------- web/web.go | 2 +- web/web_test.go | 5 +- 84 files changed, 1216 insertions(+), 1345 deletions(-) delete mode 100644 app/server_test.go delete mode 100644 cmd/platform/server_test.go create mode 100644 jobs/server_test.go delete mode 100644 model/client4_test.go delete mode 100644 templates/globalrelay_compliance_export.html delete mode 100644 templates/globalrelay_compliance_export_message.html delete mode 100644 templates/globalrelay_compliance_export_participant_row.html delete mode 100644 utils/api_test.go diff --git a/Makefile b/Makefile index 366c27057..ca8fafdb3 100644 --- a/Makefile +++ b/Makefile @@ -276,7 +276,7 @@ store-mocks: ## Creates mock files. GOPATH=$(shell go env GOPATH) $(shell go env GOPATH)/bin/mockery -dir store -all -output store/storetest/mocks -note 'Regenerate this file using `make store-mocks`.' update-jira-plugin: ## Updates Jira plugin. - go get github.com/mattermost/go-bindata/... + go get github.com/jteeuwen/go-bindata/... curl -s https://api.github.com/repos/mattermost/mattermost-plugin-jira/releases/latest | grep browser_download_url | grep darwin-amd64 | cut -d '"' -f 4 | wget -qi - -O plugin.tar.gz $(shell go env GOPATH)/bin/go-bindata -pkg jira -o app/plugin/jira/plugin_darwin_amd64.go plugin.tar.gz curl -s https://api.github.com/repos/mattermost/mattermost-plugin-jira/releases/latest | grep browser_download_url | grep linux-amd64 | cut -d '"' -f 4 | wget -qi - -O plugin.tar.gz @@ -287,7 +287,7 @@ update-jira-plugin: ## Updates Jira plugin. gofmt -s -w ./app/plugin/jira update-zoom-plugin: ## Updates Zoom plugin. - go get github.com/mattermost/go-bindata/... + go get github.com/jteeuwen/go-bindata/... curl -s https://api.github.com/repos/mattermost/mattermost-plugin-zoom/releases/latest | grep browser_download_url | grep darwin-amd64 | cut -d '"' -f 4 | wget -qi - -O plugin.tar.gz $(shell go env GOPATH)/bin/go-bindata -pkg zoom -o app/plugin/zoom/plugin_darwin_amd64.go plugin.tar.gz curl -s https://api.github.com/repos/mattermost/mattermost-plugin-zoom/releases/latest | grep browser_download_url | grep linux-amd64 | cut -d '"' -f 4 | wget -qi - -O plugin.tar.gz diff --git a/api/api.go b/api/api.go index 70f36db85..2d65bb216 100644 --- a/api/api.go +++ b/api/api.go @@ -109,7 +109,7 @@ func Init(a *app.App, root *mux.Router) *API { api.InitReaction() // 404 on any api route before web.go has a chance to serve it - root.Handle("/api/{anything:.*}", http.HandlerFunc(api.Handle404)) + root.Handle("/api/{anything:.*}", http.HandlerFunc(Handle404)) a.InitEmailBatching() @@ -120,10 +120,6 @@ func Init(a *app.App, root *mux.Router) *API { return api } -func (api *API) Handle404(w http.ResponseWriter, r *http.Request) { - Handle404(api.App, w, r) -} - func ReturnStatusOK(w http.ResponseWriter) { m := make(map[string]string) m[model.STATUS] = model.STATUS_OK diff --git a/api/apitestlib.go b/api/apitestlib.go index 47691b2dd..8d7f54902 100644 --- a/api/apitestlib.go +++ b/api/apitestlib.go @@ -105,11 +105,7 @@ func setupTestHelper(enterprise bool) *TestHelper { if testStore != nil { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = ":0" }) } - serverErr := th.App.StartServer() - if serverErr != nil { - panic(serverErr) - } - + th.App.StartServer() th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = prevListenAddress }) api4.Init(th.App, th.App.Srv.Router, false) Init(th.App, th.App.Srv.Router) @@ -118,10 +114,9 @@ func setupTestHelper(enterprise bool) *TestHelper { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.EnableOpenServer = true }) + utils.SetIsLicensed(enterprise) if enterprise { - th.App.SetLicense(model.NewTestLicense()) - } else { - th.App.SetLicense(nil) + utils.License().Features.SetDefaults() } return th diff --git a/api/channel_test.go b/api/channel_test.go index 9268d9071..5533105c3 100644 --- a/api/channel_test.go +++ b/api/channel_test.go @@ -12,6 +12,7 @@ import ( "github.com/mattermost/mattermost-server/model" "github.com/mattermost/mattermost-server/store" "github.com/mattermost/mattermost-server/store/sqlstore" + "github.com/mattermost/mattermost-server/utils" ) func TestCreateChannel(t *testing.T) { @@ -96,9 +97,23 @@ func TestCreateChannel(t *testing.T) { t.Fatal("Should have errored out on direct channel type") } + isLicensed := utils.IsLicensed() + license := utils.License() + restrictPublicChannel := *th.App.Config().TeamSettings.RestrictPublicChannelManagement + restrictPrivateChannel := *th.App.Config().TeamSettings.RestrictPrivateChannelManagement + defer func() { + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelManagement = restrictPublicChannel }) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManagement = restrictPrivateChannel }) + utils.SetIsLicensed(isLicensed) + utils.SetLicense(license) + th.App.SetDefaultRolesBasedOnConfig() + }() th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_ALL }) th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_ALL }) - th.App.SetLicense(model.NewTestLicense()) + th.App.SetDefaultRolesBasedOnConfig() + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() channel2 := &model.Channel{DisplayName: "Test API Name", Name: "zz" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel3 := &model.Channel{DisplayName: "Test API Name", Name: "zz" + model.NewId() + "a", Type: model.CHANNEL_PRIVATE, TeamId: team.Id} @@ -115,6 +130,7 @@ func TestCreateChannel(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelCreation = model.PERMISSIONS_TEAM_ADMIN }) + th.App.SetDefaultRolesBasedOnConfig() th.LoginBasic2() channel2.Name = "zz" + model.NewId() + "a" @@ -144,6 +160,7 @@ func TestCreateChannel(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelCreation = model.PERMISSIONS_SYSTEM_ADMIN }) + th.App.SetDefaultRolesBasedOnConfig() channel2.Name = "zz" + model.NewId() + "a" channel3.Name = "zz" + model.NewId() + "a" @@ -164,7 +181,9 @@ func TestCreateChannel(t *testing.T) { } // Check that if unlicensed the policy restriction is not enforced. - th.App.SetLicense(nil) + utils.SetIsLicensed(false) + utils.SetLicense(nil) + th.App.SetDefaultRolesBasedOnConfig() channel4 := model.Channel{DisplayName: "Test API Name", Name: "zz" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel5 := model.Channel{DisplayName: "Test API Name", Name: "zz" + model.NewId() + "a", Type: model.CHANNEL_PRIVATE, TeamId: team.Id} @@ -177,6 +196,7 @@ func TestCreateChannel(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelCreation = model.PERMISSIONS_ALL }) th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelCreation = model.PERMISSIONS_ALL }) + th.App.SetDefaultRolesBasedOnConfig() } func TestCreateDirectChannel(t *testing.T) { @@ -347,9 +367,23 @@ func TestUpdateChannel(t *testing.T) { t.Fatal("should have failed - channel deleted") } + isLicensed := utils.IsLicensed() + license := utils.License() + restrictPublicChannel := *th.App.Config().TeamSettings.RestrictPublicChannelManagement + restrictPrivateChannel := *th.App.Config().TeamSettings.RestrictPrivateChannelManagement + defer func() { + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelManagement = restrictPublicChannel }) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManagement = restrictPrivateChannel }) + utils.SetIsLicensed(isLicensed) + utils.SetLicense(license) + th.App.SetDefaultRolesBasedOnConfig() + }() th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_ALL }) th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_ALL }) - th.App.SetLicense(model.NewTestLicense()) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + th.App.SetDefaultRolesBasedOnConfig() channel2 := th.CreateChannel(Client, team) channel3 := th.CreatePrivateChannel(Client, team) @@ -370,8 +404,14 @@ func TestUpdateChannel(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_CHANNEL_ADMIN + }) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_CHANNEL_ADMIN }) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + th.App.SetDefaultRolesBasedOnConfig() th.MakeUserChannelUser(th.BasicUser, channel2) th.MakeUserChannelUser(th.BasicUser, channel3) sqlstore.ClearChannelCaches() @@ -407,9 +447,14 @@ func TestUpdateChannel(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_TEAM_ADMIN + }) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_TEAM_ADMIN }) - th.App.SetLicense(model.NewTestLicense()) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + th.App.SetDefaultRolesBasedOnConfig() if _, err := Client.UpdateChannel(channel2); err == nil { t.Fatal("should have errored not team admin") @@ -432,8 +477,14 @@ func TestUpdateChannel(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_SYSTEM_ADMIN + }) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_SYSTEM_ADMIN }) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + th.App.SetDefaultRolesBasedOnConfig() if _, err := Client.UpdateChannel(channel2); err == nil { t.Fatal("should have errored not system admin") @@ -452,7 +503,9 @@ func TestUpdateChannel(t *testing.T) { } // Check that if unlicensed the policy restriction is not enforced. - th.App.SetLicense(nil) + utils.SetIsLicensed(false) + utils.SetLicense(nil) + th.App.SetDefaultRolesBasedOnConfig() if _, err := Client.UpdateChannel(channel2); err != nil { t.Fatal(err) @@ -565,9 +618,23 @@ func TestUpdateChannelHeader(t *testing.T) { t.Fatal("should have errored non-channel member trying to update header") } + isLicensed := utils.IsLicensed() + license := utils.License() + restrictPublicChannel := *th.App.Config().TeamSettings.RestrictPublicChannelManagement + restrictPrivateChannel := *th.App.Config().TeamSettings.RestrictPrivateChannelManagement + defer func() { + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelManagement = restrictPublicChannel }) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManagement = restrictPrivateChannel }) + utils.SetIsLicensed(isLicensed) + utils.SetLicense(license) + th.App.SetDefaultRolesBasedOnConfig() + }() th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_ALL }) th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_ALL }) - th.App.SetLicense(model.NewTestLicense()) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + th.App.SetDefaultRolesBasedOnConfig() th.LoginBasic() channel2 := th.CreateChannel(Client, team) @@ -592,8 +659,11 @@ func TestUpdateChannelHeader(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_CHANNEL_ADMIN + }) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_CHANNEL_ADMIN }) + th.App.SetDefaultRolesBasedOnConfig() th.MakeUserChannelUser(th.BasicUser, channel2) th.MakeUserChannelUser(th.BasicUser, channel3) sqlstore.ClearChannelCaches() @@ -618,8 +688,11 @@ func TestUpdateChannelHeader(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_TEAM_ADMIN + }) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_TEAM_ADMIN }) + th.App.SetDefaultRolesBasedOnConfig() if _, err := Client.UpdateChannelHeader(data2); err == nil { t.Fatal("should have errored not team admin") @@ -642,8 +715,11 @@ func TestUpdateChannelHeader(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_SYSTEM_ADMIN + }) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_SYSTEM_ADMIN }) + th.App.SetDefaultRolesBasedOnConfig() if _, err := Client.UpdateChannelHeader(data2); err == nil { t.Fatal("should have errored not system admin") @@ -665,7 +741,9 @@ func TestUpdateChannelHeader(t *testing.T) { } // Check that if unlicensed the policy restriction is not enforced. - th.App.SetLicense(nil) + utils.SetIsLicensed(false) + utils.SetLicense(nil) + th.App.SetDefaultRolesBasedOnConfig() if _, err := SystemAdminClient.UpdateChannelHeader(data2); err != nil { t.Fatal(err) @@ -736,9 +814,23 @@ func TestUpdateChannelPurpose(t *testing.T) { t.Fatal("should have errored non-channel member trying to update purpose") } + isLicensed := utils.IsLicensed() + license := utils.License() + restrictPublicChannel := *th.App.Config().TeamSettings.RestrictPublicChannelManagement + restrictPrivateChannel := *th.App.Config().TeamSettings.RestrictPrivateChannelManagement + defer func() { + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelManagement = restrictPublicChannel }) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManagement = restrictPrivateChannel }) + utils.SetIsLicensed(isLicensed) + utils.SetLicense(license) + th.App.SetDefaultRolesBasedOnConfig() + }() th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_ALL }) th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_ALL }) - th.App.SetLicense(model.NewTestLicense()) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + th.App.SetDefaultRolesBasedOnConfig() th.LoginBasic() channel2 := th.CreateChannel(Client, team) @@ -763,8 +855,11 @@ func TestUpdateChannelPurpose(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_CHANNEL_ADMIN + }) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_CHANNEL_ADMIN }) + th.App.SetDefaultRolesBasedOnConfig() th.MakeUserChannelUser(th.BasicUser, channel2) th.MakeUserChannelUser(th.BasicUser, channel3) sqlstore.ClearChannelCaches() @@ -789,8 +884,11 @@ func TestUpdateChannelPurpose(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_TEAM_ADMIN + }) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_TEAM_ADMIN }) + th.App.SetDefaultRolesBasedOnConfig() if _, err := Client.UpdateChannelPurpose(data2); err == nil { t.Fatal("should have errored not team admin") @@ -813,8 +911,11 @@ func TestUpdateChannelPurpose(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_SYSTEM_ADMIN + }) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_SYSTEM_ADMIN }) + th.App.SetDefaultRolesBasedOnConfig() if _, err := Client.UpdateChannelPurpose(data2); err == nil { t.Fatal("should have errored not system admin") @@ -836,7 +937,9 @@ func TestUpdateChannelPurpose(t *testing.T) { } // Check that if unlicensed the policy restriction is not enforced. - th.App.SetLicense(nil) + utils.SetIsLicensed(false) + utils.SetLicense(nil) + th.App.SetDefaultRolesBasedOnConfig() if _, err := SystemAdminClient.UpdateChannelHeader(data2); err != nil { t.Fatal(err) } @@ -1297,9 +1400,23 @@ func TestDeleteChannel(t *testing.T) { t.Fatal("should have failed - channel already deleted") } + isLicensed := utils.IsLicensed() + license := utils.License() + restrictPublicChannel := *th.App.Config().TeamSettings.RestrictPublicChannelManagement + restrictPrivateChannel := *th.App.Config().TeamSettings.RestrictPrivateChannelManagement + defer func() { + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelManagement = restrictPublicChannel }) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManagement = restrictPrivateChannel }) + utils.SetIsLicensed(isLicensed) + utils.SetLicense(license) + th.App.SetDefaultRolesBasedOnConfig() + }() th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_ALL }) th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_ALL }) - th.App.SetLicense(model.NewTestLicense()) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + th.App.SetDefaultRolesBasedOnConfig() th.LoginSystemAdmin() th.LinkUserToTeam(th.BasicUser, team) @@ -1321,10 +1438,16 @@ func TestDeleteChannel(t *testing.T) { t.Fatal(err) } + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelDeletion = model.PERMISSIONS_CHANNEL_ADMIN + }) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelDeletion = model.PERMISSIONS_CHANNEL_ADMIN }) + th.App.SetDefaultRolesBasedOnConfig() th.LoginSystemAdmin() @@ -1375,11 +1498,16 @@ func TestDeleteChannel(t *testing.T) { th.UpdateUserToNonTeamAdmin(th.BasicUser, team) th.App.InvalidateAllCaches() - th.App.SetLicense(model.NewTestLicense()) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelDeletion = model.PERMISSIONS_TEAM_ADMIN + }) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelDeletion = model.PERMISSIONS_TEAM_ADMIN }) + th.App.SetDefaultRolesBasedOnConfig() th.LoginSystemAdmin() @@ -1409,10 +1537,16 @@ func TestDeleteChannel(t *testing.T) { t.Fatal(err) } + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelDeletion = model.PERMISSIONS_SYSTEM_ADMIN + }) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelDeletion = model.PERMISSIONS_SYSTEM_ADMIN }) + th.App.SetDefaultRolesBasedOnConfig() th.LoginSystemAdmin() @@ -1444,7 +1578,9 @@ func TestDeleteChannel(t *testing.T) { } // Check that if unlicensed the policy restriction is not enforced. - th.App.SetLicense(nil) + utils.SetIsLicensed(false) + utils.SetLicense(nil) + th.App.SetDefaultRolesBasedOnConfig() channel2 = th.CreateChannel(Client, team) channel3 = th.CreatePrivateChannel(Client, team) @@ -1459,6 +1595,10 @@ func TestDeleteChannel(t *testing.T) { if _, err := Client.DeleteChannel(channel3.Id); err != nil { t.Fatal(err) } + + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelDeletion = model.PERMISSIONS_ALL }) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelDeletion = model.PERMISSIONS_ALL }) + th.App.SetDefaultRolesBasedOnConfig() } func TestGetChannelStats(t *testing.T) { @@ -1535,9 +1675,16 @@ func TestAddChannelMember(t *testing.T) { } // Test policy does not apply to TE. + restrictPrivateChannel := *th.App.Config().TeamSettings.RestrictPrivateChannelManageMembers + defer func() { + th.App.UpdateConfig(func(cfg *model.Config) { + *cfg.TeamSettings.RestrictPrivateChannelManageMembers = restrictPrivateChannel + }) + }() th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_CHANNEL_ADMIN }) + th.App.SetDefaultRolesBasedOnConfig() channel3 := &model.Channel{DisplayName: "A Test API Name", Name: "zz" + model.NewId() + "a", Type: model.CHANNEL_PRIVATE, TeamId: team.Id} channel3 = Client.Must(th.SystemAdminClient.CreateChannel(channel3)).Data.(*model.Channel) @@ -1547,8 +1694,18 @@ func TestAddChannelMember(t *testing.T) { } // Add a license + isLicensed := utils.IsLicensed() + license := utils.License() + defer func() { + utils.SetIsLicensed(isLicensed) + utils.SetLicense(license) + th.App.SetDefaultRolesBasedOnConfig() + }() th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_ALL }) - th.App.SetLicense(model.NewTestLicense()) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + th.App.SetDefaultRolesBasedOnConfig() // Check that a regular channel user can add other users. channel4 := &model.Channel{DisplayName: "A Test API Name", Name: "zz" + model.NewId() + "a", Type: model.CHANNEL_PRIVATE, TeamId: team.Id} @@ -1562,6 +1719,10 @@ func TestAddChannelMember(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_CHANNEL_ADMIN }) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + th.App.SetDefaultRolesBasedOnConfig() channel5 := &model.Channel{DisplayName: "A Test API Name", Name: "zz" + model.NewId() + "a", Type: model.CHANNEL_PRIVATE, TeamId: team.Id} channel5 = Client.Must(th.SystemAdminClient.CreateChannel(channel5)).Data.(*model.Channel) @@ -1572,7 +1733,10 @@ func TestAddChannelMember(t *testing.T) { th.MakeUserChannelAdmin(user1, channel5) th.App.InvalidateAllCaches() - th.App.SetLicense(model.NewTestLicense()) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + th.App.SetDefaultRolesBasedOnConfig() if _, err := Client.AddChannelMember(channel5.Id, user2.Id); err != nil { t.Fatal(err) @@ -1582,6 +1746,10 @@ func TestAddChannelMember(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_TEAM_ADMIN }) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + th.App.SetDefaultRolesBasedOnConfig() channel6 := &model.Channel{DisplayName: "A Test API Name", Name: "zz" + model.NewId() + "a", Type: model.CHANNEL_PRIVATE, TeamId: team.Id} channel6 = Client.Must(th.SystemAdminClient.CreateChannel(channel6)).Data.(*model.Channel) @@ -1592,7 +1760,10 @@ func TestAddChannelMember(t *testing.T) { th.UpdateUserToTeamAdmin(user1, team) th.App.InvalidateAllCaches() - th.App.SetLicense(model.NewTestLicense()) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + th.App.SetDefaultRolesBasedOnConfig() if _, err := Client.AddChannelMember(channel6.Id, user2.Id); err != nil { t.Fatal(err) @@ -1602,6 +1773,10 @@ func TestAddChannelMember(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_SYSTEM_ADMIN }) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + th.App.SetDefaultRolesBasedOnConfig() channel7 := &model.Channel{DisplayName: "A Test API Name", Name: "zz" + model.NewId() + "a", Type: model.CHANNEL_PRIVATE, TeamId: team.Id} channel7 = Client.Must(th.SystemAdminClient.CreateChannel(channel7)).Data.(*model.Channel) @@ -1685,9 +1860,16 @@ func TestRemoveChannelMember(t *testing.T) { th.LoginBasic() // Test policy does not apply to TE. + restrictPrivateChannel := *th.App.Config().TeamSettings.RestrictPrivateChannelManageMembers + defer func() { + th.App.UpdateConfig(func(cfg *model.Config) { + *cfg.TeamSettings.RestrictPrivateChannelManageMembers = restrictPrivateChannel + }) + }() th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_CHANNEL_ADMIN }) + th.App.SetDefaultRolesBasedOnConfig() channel3 := &model.Channel{DisplayName: "A Test API Name", Name: "zz" + model.NewId() + "a", Type: model.CHANNEL_PRIVATE, TeamId: team.Id} channel3 = Client.Must(th.SystemAdminClient.CreateChannel(channel3)).Data.(*model.Channel) @@ -1698,8 +1880,18 @@ func TestRemoveChannelMember(t *testing.T) { } // Add a license + isLicensed := utils.IsLicensed() + license := utils.License() + defer func() { + utils.SetIsLicensed(isLicensed) + utils.SetLicense(license) + th.App.SetDefaultRolesBasedOnConfig() + }() th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_ALL }) - th.App.SetLicense(model.NewTestLicense()) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + th.App.SetDefaultRolesBasedOnConfig() // Check that a regular channel user can remove other users. channel4 := &model.Channel{DisplayName: "A Test API Name", Name: "zz" + model.NewId() + "a", Type: model.CHANNEL_PRIVATE, TeamId: team.Id} @@ -1714,6 +1906,10 @@ func TestRemoveChannelMember(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_CHANNEL_ADMIN }) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + th.App.SetDefaultRolesBasedOnConfig() channel5 := &model.Channel{DisplayName: "A Test API Name", Name: "zz" + model.NewId() + "a", Type: model.CHANNEL_PRIVATE, TeamId: team.Id} channel5 = Client.Must(th.SystemAdminClient.CreateChannel(channel5)).Data.(*model.Channel) @@ -1725,7 +1921,9 @@ func TestRemoveChannelMember(t *testing.T) { th.MakeUserChannelAdmin(user1, channel5) th.App.InvalidateAllCaches() - th.App.SetLicense(model.NewTestLicense()) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() if _, err := Client.RemoveChannelMember(channel5.Id, user2.Id); err != nil { t.Fatal(err) @@ -1735,6 +1933,10 @@ func TestRemoveChannelMember(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_TEAM_ADMIN }) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + th.App.SetDefaultRolesBasedOnConfig() channel6 := &model.Channel{DisplayName: "A Test API Name", Name: "zz" + model.NewId() + "a", Type: model.CHANNEL_PRIVATE, TeamId: team.Id} channel6 = Client.Must(th.SystemAdminClient.CreateChannel(channel6)).Data.(*model.Channel) @@ -1746,7 +1948,10 @@ func TestRemoveChannelMember(t *testing.T) { th.UpdateUserToTeamAdmin(user1, team) th.App.InvalidateAllCaches() - th.App.SetLicense(model.NewTestLicense()) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + th.App.SetDefaultRolesBasedOnConfig() if _, err := Client.RemoveChannelMember(channel6.Id, user2.Id); err != nil { t.Fatal(err) @@ -1756,6 +1961,10 @@ func TestRemoveChannelMember(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_SYSTEM_ADMIN }) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + th.App.SetDefaultRolesBasedOnConfig() channel7 := &model.Channel{DisplayName: "A Test API Name", Name: "zz" + model.NewId() + "a", Type: model.CHANNEL_PRIVATE, TeamId: team.Id} channel7 = Client.Must(th.SystemAdminClient.CreateChannel(channel7)).Data.(*model.Channel) diff --git a/api/context.go b/api/context.go index a8ff2b694..b28a24731 100644 --- a/api/context.go +++ b/api/context.go @@ -229,7 +229,7 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if c.Err.StatusCode == http.StatusUnauthorized { http.Redirect(w, r, c.GetTeamURL()+"/?redirect="+url.QueryEscape(r.URL.Path), http.StatusTemporaryRedirect) } else { - utils.RenderWebAppError(w, r, c.Err, c.App.AsymmetricSigningKey()) + utils.RenderWebError(c.Err, w, r) } } @@ -434,7 +434,7 @@ func IsApiCall(r *http.Request) bool { return strings.Index(r.URL.Path, "/api/") == 0 } -func Handle404(a *app.App, w http.ResponseWriter, r *http.Request) { +func Handle404(w http.ResponseWriter, r *http.Request) { err := model.NewAppError("Handle404", "api.context.404.app_error", nil, "", http.StatusNotFound) l4g.Debug("%v: code=404 ip=%v", r.URL.Path, utils.GetIpAddress(r)) @@ -444,7 +444,7 @@ func Handle404(a *app.App, w http.ResponseWriter, r *http.Request) { 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 { - utils.RenderWebAppError(w, r, err, a.AsymmetricSigningKey()) + utils.RenderWebError(err, w, r) } } diff --git a/api/file.go b/api/file.go index 3b8984816..2d626304e 100644 --- a/api/file.go +++ b/api/file.go @@ -174,12 +174,12 @@ func getPublicFile(c *Context, w http.ResponseWriter, r *http.Request) { if hash != correctHash { c.Err = model.NewAppError("getPublicFile", "api.file.get_file.public_invalid.app_error", nil, "", http.StatusBadRequest) - utils.RenderWebAppError(w, r, c.Err, c.App.AsymmetricSigningKey()) + http.Redirect(w, r, c.GetSiteURLHeader()+"/error?message="+utils.T(c.Err.Message), http.StatusTemporaryRedirect) return } } else { c.Err = model.NewAppError("getPublicFile", "api.file.get_file.public_invalid.app_error", nil, "", http.StatusBadRequest) - utils.RenderWebAppError(w, r, c.Err, c.App.AsymmetricSigningKey()) + http.Redirect(w, r, c.GetSiteURLHeader()+"/error?message="+utils.T(c.Err.Message), http.StatusTemporaryRedirect) return } diff --git a/api/license.go b/api/license.go index 432442ad6..8eb7803e1 100644 --- a/api/license.go +++ b/api/license.go @@ -9,6 +9,7 @@ import ( "net/http" "github.com/mattermost/mattermost-server/model" + "github.com/mattermost/mattermost-server/utils" ) func (api *API) InitLicense() { @@ -82,7 +83,7 @@ func removeLicense(c *Context, w http.ResponseWriter, r *http.Request) { func getClientLicenceConfig(c *Context, w http.ResponseWriter, r *http.Request) { useSanitizedLicense := !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) - etag := c.App.GetClientLicenseEtag(useSanitizedLicense) + etag := utils.GetClientLicenseEtag(useSanitizedLicense) if c.HandleEtag(etag, "Get Client License Config", w, r) { return } @@ -90,9 +91,9 @@ func getClientLicenceConfig(c *Context, w http.ResponseWriter, r *http.Request) var clientLicense map[string]string if useSanitizedLicense { - clientLicense = c.App.ClientLicense() + clientLicense = utils.ClientLicense() } else { - clientLicense = c.App.GetSanitizedClientLicense() + clientLicense = utils.GetSanitizedClientLicense() } w.Header().Set(model.HEADER_ETAG_SERVER, etag) diff --git a/api/license_test.go b/api/license_test.go index 47586151a..50d73101d 100644 --- a/api/license_test.go +++ b/api/license_test.go @@ -5,6 +5,8 @@ package api import ( "testing" + + "github.com/mattermost/mattermost-server/utils" ) func TestGetLicenceConfig(t *testing.T) { @@ -30,7 +32,7 @@ func TestGetLicenceConfig(t *testing.T) { t.Fatal("cache should be empty") } - th.App.SetClientLicense(map[string]string{"IsLicensed": "true"}) + utils.SetClientLicense(map[string]string{"IsLicensed": "true"}) if cache_result, err := Client.GetClientLicenceConfig(result.Etag); err != nil { t.Fatal(err) @@ -38,7 +40,7 @@ func TestGetLicenceConfig(t *testing.T) { t.Fatal("result should not be empty") } - th.App.SetClientLicense(map[string]string{"SomeFeature": "true", "IsLicensed": "true"}) + utils.SetClientLicense(map[string]string{"SomeFeature": "true", "IsLicensed": "true"}) if cache_result, err := Client.GetClientLicenceConfig(result.Etag); err != nil { t.Fatal(err) @@ -46,6 +48,6 @@ func TestGetLicenceConfig(t *testing.T) { t.Fatal("result should not be empty") } - th.App.SetClientLicense(map[string]string{"IsLicensed": "false"}) + utils.SetClientLicense(map[string]string{"IsLicensed": "false"}) } } diff --git a/api/post_test.go b/api/post_test.go index 2fc79d9b1..299fdf046 100644 --- a/api/post_test.go +++ b/api/post_test.go @@ -160,8 +160,20 @@ func TestCreatePost(t *testing.T) { } } + isLicensed := utils.IsLicensed() + license := utils.License() + disableTownSquareReadOnly := th.App.Config().TeamSettings.ExperimentalTownSquareIsReadOnly + defer func() { + th.App.UpdateConfig(func(cfg *model.Config) { cfg.TeamSettings.ExperimentalTownSquareIsReadOnly = disableTownSquareReadOnly }) + utils.SetIsLicensed(isLicensed) + utils.SetLicense(license) + th.App.SetDefaultRolesBasedOnConfig() + }() th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.ExperimentalTownSquareIsReadOnly = true }) - th.App.SetLicense(model.NewTestLicense()) + th.App.SetDefaultRolesBasedOnConfig() + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() defaultChannel := store.Must(th.App.Srv.Store.Channel().GetByName(team.Id, model.DEFAULT_CHANNEL, true)).(*model.Channel) defaultPost := &model.Post{ @@ -454,7 +466,16 @@ func TestUpdatePost(t *testing.T) { } // Test licensed policy controls for edit post - th.App.SetLicense(model.NewTestLicense()) + isLicensed := utils.IsLicensed() + license := utils.License() + defer func() { + utils.SetIsLicensed(isLicensed) + utils.SetLicense(license) + }() + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.AllowEditPost = model.ALLOW_EDIT_POST_NEVER }) post4 := &model.Post{ChannelId: channel1.Id, Message: "zz" + model.NewId() + "a", RootId: rpost1.Data.(*model.Post).Id} @@ -945,6 +966,7 @@ func TestDeletePosts(t *testing.T) { team1 := th.BasicTeam th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.RestrictPostDelete = model.PERMISSIONS_DELETE_POST_ALL }) + th.App.SetDefaultRolesBasedOnConfig() time.Sleep(10 * time.Millisecond) post1 := &model.Post{ChannelId: channel1.Id, Message: "zz" + model.NewId() + "a"} @@ -999,7 +1021,15 @@ func TestDeletePosts(t *testing.T) { } // Test licensed policy controls for delete post - th.App.SetLicense(model.NewTestLicense()) + isLicensed := utils.IsLicensed() + license := utils.License() + defer func() { + utils.SetIsLicensed(isLicensed) + utils.SetLicense(license) + }() + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() th.UpdateUserToTeamAdmin(th.BasicUser2, th.BasicTeam) @@ -1014,6 +1044,7 @@ func TestDeletePosts(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.RestrictPostDelete = model.PERMISSIONS_DELETE_POST_TEAM_ADMIN }) + th.App.SetDefaultRolesBasedOnConfig() th.LoginBasic() @@ -1038,6 +1069,7 @@ func TestDeletePosts(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.RestrictPostDelete = model.PERMISSIONS_DELETE_POST_SYSTEM_ADMIN }) + th.App.SetDefaultRolesBasedOnConfig() th.LoginBasic() @@ -1056,7 +1088,9 @@ func TestDeletePosts(t *testing.T) { } // Check that if unlicensed the policy restriction is not enforced. - th.App.SetLicense(nil) + utils.SetIsLicensed(false) + utils.SetLicense(nil) + th.App.SetDefaultRolesBasedOnConfig() time.Sleep(10 * time.Millisecond) post7 := &model.Post{ChannelId: channel1.Id, Message: "zz" + model.NewId() + "a"} diff --git a/api/team_test.go b/api/team_test.go index 696cf31bb..b1c892544 100644 --- a/api/team_test.go +++ b/api/team_test.go @@ -139,8 +139,20 @@ func TestAddUserToTeam(t *testing.T) { t.Fatal(err) } + // Restore config/license at end of test case. + restrictTeamInvite := *th.App.Config().TeamSettings.RestrictTeamInvite + isLicensed := utils.IsLicensed() + license := utils.License() + defer func() { + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictTeamInvite = restrictTeamInvite }) + utils.SetIsLicensed(isLicensed) + utils.SetLicense(license) + th.App.SetDefaultRolesBasedOnConfig() + }() + // Set the config so that only team admins can add a user to a team. th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictTeamInvite = model.PERMISSIONS_TEAM_ADMIN }) + th.App.SetDefaultRolesBasedOnConfig() // Test without the EE license to see that the permission restriction is ignored. user3 := th.CreateUser(th.BasicClient) @@ -149,7 +161,10 @@ func TestAddUserToTeam(t *testing.T) { } // Add an EE license. - th.App.SetLicense(model.NewTestLicense()) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + th.App.SetDefaultRolesBasedOnConfig() // Check that a regular user can't add someone to the team. user4 := th.CreateUser(th.BasicClient) @@ -160,8 +175,11 @@ func TestAddUserToTeam(t *testing.T) { // Should work as team admin. th.UpdateUserToTeamAdmin(th.BasicUser, th.BasicTeam) th.App.InvalidateAllCaches() - th.App.SetLicense(model.NewTestLicense()) th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictTeamInvite = model.PERMISSIONS_TEAM_ADMIN }) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + th.App.SetDefaultRolesBasedOnConfig() user5 := th.CreateUser(th.BasicClient) if _, err := th.BasicClient.AddUserToTeam(th.BasicTeam.Id, user5.Id); err != nil { @@ -170,6 +188,7 @@ func TestAddUserToTeam(t *testing.T) { // Change permission level to System Admin th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictTeamInvite = model.PERMISSIONS_SYSTEM_ADMIN }) + th.App.SetDefaultRolesBasedOnConfig() // Should not work as team admin. user6 := th.CreateUser(th.BasicClient) @@ -546,7 +565,13 @@ func TestInviteMembers(t *testing.T) { t.Fatal("Should have errored out on no invites to send") } + restrictTeamInvite := *th.App.Config().TeamSettings.RestrictTeamInvite + defer func() { + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictTeamInvite = restrictTeamInvite }) + th.App.SetDefaultRolesBasedOnConfig() + }() th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictTeamInvite = model.PERMISSIONS_TEAM_ADMIN }) + th.App.SetDefaultRolesBasedOnConfig() th.LoginBasic2() th.LinkUserToTeam(th.BasicUser2, team) @@ -555,7 +580,17 @@ func TestInviteMembers(t *testing.T) { t.Fatal(err) } - th.App.SetLicense(model.NewTestLicense()) + isLicensed := utils.IsLicensed() + license := utils.License() + defer func() { + utils.SetIsLicensed(isLicensed) + utils.SetLicense(license) + th.App.SetDefaultRolesBasedOnConfig() + }() + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + th.App.SetDefaultRolesBasedOnConfig() if _, err := Client.InviteMembers(invites); err == nil { t.Fatal("should have errored not team admin and licensed") @@ -571,6 +606,7 @@ func TestInviteMembers(t *testing.T) { } th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictTeamInvite = model.PERMISSIONS_SYSTEM_ADMIN }) + th.App.SetDefaultRolesBasedOnConfig() if _, err := Client.InviteMembers(invites); err == nil { t.Fatal("should have errored not system admin and licensed") diff --git a/api/user.go b/api/user.go index ad4f12ef3..440ea5858 100644 --- a/api/user.go +++ b/api/user.go @@ -299,9 +299,9 @@ func getInitialLoad(c *Context, w http.ResponseWriter, r *http.Request) { il.ClientCfg = c.App.ClientConfig() if c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) { - il.LicenseCfg = c.App.ClientLicense() + il.LicenseCfg = utils.ClientLicense() } else { - il.LicenseCfg = c.App.GetSanitizedClientLicense() + il.LicenseCfg = utils.GetSanitizedClientLicense() } w.Write([]byte(il.ToJson())) diff --git a/api/user_test.go b/api/user_test.go index f65d7c45b..8d6aad22b 100644 --- a/api/user_test.go +++ b/api/user_test.go @@ -1889,7 +1889,17 @@ func TestUpdateMfa(t *testing.T) { Client := th.BasicClient - th.App.SetLicense(nil) + isLicensed := utils.IsLicensed() + license := utils.License() + defer func() { + utils.SetIsLicensed(isLicensed) + utils.SetLicense(license) + }() + utils.SetIsLicensed(false) + utils.SetLicense(&model.License{Features: &model.Features{}}) + if utils.License().Features.MFA == nil { + utils.License().Features.MFA = new(bool) + } team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} rteam, _ := Client.CreateTeam(&team) @@ -1915,7 +1925,8 @@ func TestUpdateMfa(t *testing.T) { t.Fatal("should have failed - not licensed") } - th.App.SetLicense(model.NewTestLicense("mfa")) + utils.SetIsLicensed(true) + *utils.License().Features.MFA = true th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableMultifactorAuthentication = true }) if _, err := Client.UpdateMfa(true, "123456"); err == nil { diff --git a/api/webhook_test.go b/api/webhook_test.go index f4d46496b..b5a836603 100644 --- a/api/webhook_test.go +++ b/api/webhook_test.go @@ -9,6 +9,7 @@ import ( "testing" "github.com/mattermost/mattermost-server/model" + "github.com/mattermost/mattermost-server/utils" ) func TestCreateIncomingHook(t *testing.T) { @@ -92,6 +93,7 @@ func TestCreateIncomingHook(t *testing.T) { } th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) + th.App.SetDefaultRolesBasedOnConfig() if _, err := Client.CreateIncomingWebhook(hook); err != nil { t.Fatal(err) @@ -131,6 +133,7 @@ func TestUpdateIncomingHook(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableIncomingWebhooks = true }) th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true }) + th.App.SetDefaultRolesBasedOnConfig() hook := createIncomingWebhook(channel1.Id, Client, t) @@ -214,6 +217,7 @@ func TestUpdateIncomingHook(t *testing.T) { t.Run("OnlyAdminIntegrationsDisabled", func(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) + th.App.SetDefaultRolesBasedOnConfig() t.Run("UpdateHookOfSameUser", func(t *testing.T) { sameUserHook := &model.IncomingWebhook{ChannelId: channel1.Id, UserId: user2.Id} @@ -236,6 +240,7 @@ func TestUpdateIncomingHook(t *testing.T) { }) th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true }) + th.App.SetDefaultRolesBasedOnConfig() Client.Logout() th.UpdateUserToTeamAdmin(user2, team) @@ -319,6 +324,7 @@ func TestListIncomingHooks(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableIncomingWebhooks = true }) th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true }) + th.App.SetDefaultRolesBasedOnConfig() hook1 := &model.IncomingWebhook{ChannelId: channel1.Id} hook1 = Client.Must(Client.CreateIncomingWebhook(hook1)).Data.(*model.IncomingWebhook) @@ -345,6 +351,7 @@ func TestListIncomingHooks(t *testing.T) { } th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) + th.App.SetDefaultRolesBasedOnConfig() if _, err := Client.ListIncomingWebhooks(); err != nil { t.Fatal(err) @@ -369,6 +376,7 @@ func TestDeleteIncomingHook(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableIncomingWebhooks = true }) th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true }) + th.App.SetDefaultRolesBasedOnConfig() hook := &model.IncomingWebhook{ChannelId: channel1.Id} hook = Client.Must(Client.CreateIncomingWebhook(hook)).Data.(*model.IncomingWebhook) @@ -402,6 +410,7 @@ func TestDeleteIncomingHook(t *testing.T) { } th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) + th.App.SetDefaultRolesBasedOnConfig() if _, err := Client.DeleteIncomingWebhook(hook.Id); err == nil { t.Fatal("should have failed - not creator or team admin") @@ -438,6 +447,7 @@ func TestCreateOutgoingHook(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOutgoingWebhooks = true }) th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true }) + th.App.SetDefaultRolesBasedOnConfig() hook := &model.OutgoingWebhook{ChannelId: channel1.Id, CallbackURLs: []string{"http://nowhere.com"}} @@ -508,6 +518,7 @@ func TestCreateOutgoingHook(t *testing.T) { } th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) + th.App.SetDefaultRolesBasedOnConfig() if _, err := Client.CreateOutgoingWebhook(hook); err != nil { t.Fatal(err) @@ -540,6 +551,7 @@ func TestListOutgoingHooks(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOutgoingWebhooks = true }) th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true }) + th.App.SetDefaultRolesBasedOnConfig() hook1 := &model.OutgoingWebhook{ChannelId: channel1.Id, CallbackURLs: []string{"http://nowhere.com"}} hook1 = Client.Must(Client.CreateOutgoingWebhook(hook1)).Data.(*model.OutgoingWebhook) @@ -566,6 +578,7 @@ func TestListOutgoingHooks(t *testing.T) { } th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) + th.App.SetDefaultRolesBasedOnConfig() if _, err := Client.ListOutgoingWebhooks(); err != nil { t.Fatal(err) @@ -596,6 +609,7 @@ func TestUpdateOutgoingHook(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOutgoingWebhooks = true }) th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true }) + th.App.SetDefaultRolesBasedOnConfig() hook := createOutgoingWebhook(channel1.Id, []string{"http://nowhere.com"}, []string{"cats"}, Client, t) createOutgoingWebhook(channel1.Id, []string{"http://nowhere.com"}, []string{"dogs"}, Client, t) @@ -669,6 +683,7 @@ func TestUpdateOutgoingHook(t *testing.T) { } th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) + th.App.SetDefaultRolesBasedOnConfig() hook2 := createOutgoingWebhook(channel1.Id, []string{"http://nowhereelse.com"}, []string{"dogs"}, Client, t) if _, err := Client.UpdateOutgoingWebhook(hook2); err != nil { @@ -676,6 +691,7 @@ func TestUpdateOutgoingHook(t *testing.T) { } th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true }) + th.App.SetDefaultRolesBasedOnConfig() Client.Logout() th.LinkUserToTeam(user3, team) @@ -763,6 +779,7 @@ func TestDeleteOutgoingHook(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOutgoingWebhooks = true }) th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true }) + th.App.SetDefaultRolesBasedOnConfig() hook := &model.OutgoingWebhook{ChannelId: channel1.Id, CallbackURLs: []string{"http://nowhere.com"}} hook = Client.Must(Client.CreateOutgoingWebhook(hook)).Data.(*model.OutgoingWebhook) @@ -796,6 +813,7 @@ func TestDeleteOutgoingHook(t *testing.T) { } th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) + th.App.SetDefaultRolesBasedOnConfig() if _, err := Client.DeleteOutgoingWebhook(hook.Id); err == nil { t.Fatal("should have failed - not creator or team admin") @@ -830,6 +848,7 @@ func TestRegenOutgoingHookToken(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOutgoingWebhooks = true }) th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true }) + th.App.SetDefaultRolesBasedOnConfig() hook := &model.OutgoingWebhook{ChannelId: channel1.Id, CallbackURLs: []string{"http://nowhere.com"}} hook = Client.Must(Client.CreateOutgoingWebhook(hook)).Data.(*model.OutgoingWebhook) @@ -864,6 +883,7 @@ func TestRegenOutgoingHookToken(t *testing.T) { } th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) + th.App.SetDefaultRolesBasedOnConfig() hook = &model.OutgoingWebhook{ChannelId: channel1.Id, CallbackURLs: []string{"http://nowhere.com"}} hook = Client.Must(Client.CreateOutgoingWebhook(hook)).Data.(*model.OutgoingWebhook) @@ -897,6 +917,10 @@ func TestIncomingWebhooks(t *testing.T) { user2 := th.CreateUser(Client) th.LinkUserToTeam(user2, team) + enableIncomingHooks := th.App.Config().ServiceSettings.EnableIncomingWebhooks + defer func() { + th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableIncomingWebhooks = enableIncomingHooks }) + }() th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableIncomingWebhooks = true }) hook := &model.IncomingWebhook{ChannelId: channel1.Id} @@ -938,8 +962,20 @@ func TestIncomingWebhooks(t *testing.T) { t.Fatal("should not have failed -- ExperimentalTownSquareIsReadOnly is false and it's not a read only channel") } + isLicensed := utils.IsLicensed() + license := utils.License() + disableTownSquareReadOnly := th.App.Config().TeamSettings.ExperimentalTownSquareIsReadOnly + defer func() { + th.App.UpdateConfig(func(cfg *model.Config) { cfg.TeamSettings.ExperimentalTownSquareIsReadOnly = disableTownSquareReadOnly }) + utils.SetIsLicensed(isLicensed) + utils.SetLicense(license) + th.App.SetDefaultRolesBasedOnConfig() + }() th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.ExperimentalTownSquareIsReadOnly = true }) - th.App.SetLicense(model.NewTestLicense()) + th.App.SetDefaultRolesBasedOnConfig() + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() if _, err := th.BasicClient.DoPost(url, fmt.Sprintf("{\"text\":\"this is a test\", \"channel\":\"%s\"}", model.DEFAULT_CHANNEL), "application/json"); err == nil { t.Fatal("should have failed -- ExperimentalTownSquareIsReadOnly is true and it's a read only channel") diff --git a/api4/apitestlib.go b/api4/apitestlib.go index db43a6512..a7e64ae84 100644 --- a/api4/apitestlib.go +++ b/api4/apitestlib.go @@ -113,11 +113,7 @@ func setupTestHelper(enterprise bool) *TestHelper { if testStore != nil { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = ":0" }) } - serverErr := th.App.StartServer() - if serverErr != nil { - panic(serverErr) - } - + th.App.StartServer() th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = prevListenAddress }) Init(th.App, th.App.Srv.Router, true) wsapi.Init(th.App, th.App.Srv.WebSocketRouter) @@ -125,10 +121,9 @@ func setupTestHelper(enterprise bool) *TestHelper { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.EnableOpenServer = true }) + utils.SetIsLicensed(enterprise) if enterprise { - th.App.SetLicense(model.NewTestLicense()) - } else { - th.App.SetLicense(nil) + utils.License().Features.SetDefaults() } th.Client = th.CreateClient() @@ -303,7 +298,8 @@ func (me *TestHelper) CreateUserWithClient(client *model.Client4) *model.User { } utils.DisableDebugLogForTest() - ruser, _ := client.CreateUser(user) + ruser, r := client.CreateUser(user) + fmt.Println(r) ruser.Password = "Password1" store.Must(me.App.Srv.Store.User().VerifyEmail(ruser.Id)) utils.EnableDebugLogForTest() diff --git a/api4/channel_test.go b/api4/channel_test.go index e65918707..724b0d84b 100644 --- a/api4/channel_test.go +++ b/api4/channel_test.go @@ -14,6 +14,7 @@ import ( "github.com/mattermost/mattermost-server/model" "github.com/mattermost/mattermost-server/store/sqlstore" + "github.com/mattermost/mattermost-server/utils" ) func TestCreateChannel(t *testing.T) { @@ -81,9 +82,23 @@ func TestCreateChannel(t *testing.T) { th.LoginBasic() // Check permissions with policy config changes + isLicensed := utils.IsLicensed() + license := utils.License() + restrictPublicChannel := *th.App.Config().TeamSettings.RestrictPublicChannelCreation + restrictPrivateChannel := *th.App.Config().TeamSettings.RestrictPrivateChannelCreation + defer func() { + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelCreation = restrictPublicChannel }) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelCreation = restrictPrivateChannel }) + utils.SetIsLicensed(isLicensed) + utils.SetLicense(license) + th.App.SetDefaultRolesBasedOnConfig() + }() th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelCreation = model.PERMISSIONS_ALL }) th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelCreation = model.PERMISSIONS_ALL }) - th.App.SetLicense(model.NewTestLicense()) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + th.App.SetDefaultRolesBasedOnConfig() channel.Name = GenerateTestChannelName() _, resp = Client.CreateChannel(channel) @@ -95,8 +110,11 @@ func TestCreateChannel(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelCreation = model.PERMISSIONS_TEAM_ADMIN + }) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelCreation = model.PERMISSIONS_TEAM_ADMIN }) + th.App.SetDefaultRolesBasedOnConfig() _, resp = Client.CreateChannel(channel) CheckForbiddenStatus(t, resp) @@ -124,8 +142,11 @@ func TestCreateChannel(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelCreation = model.PERMISSIONS_SYSTEM_ADMIN + }) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelCreation = model.PERMISSIONS_SYSTEM_ADMIN }) + th.App.SetDefaultRolesBasedOnConfig() th.LoginBasic() @@ -152,7 +173,9 @@ func TestCreateChannel(t *testing.T) { CheckNoError(t, resp) // Check that if unlicensed the policy restriction is not enforced. - th.App.SetLicense(nil) + utils.SetIsLicensed(false) + utils.SetLicense(nil) + th.App.SetDefaultRolesBasedOnConfig() channel.Name = GenerateTestChannelName() _, resp = Client.CreateChannel(channel) @@ -864,9 +887,23 @@ func TestDeleteChannel(t *testing.T) { th.InitBasic().InitSystemAdmin() + isLicensed := utils.IsLicensed() + license := utils.License() + restrictPublicChannel := *th.App.Config().TeamSettings.RestrictPublicChannelManagement + restrictPrivateChannel := *th.App.Config().TeamSettings.RestrictPrivateChannelManagement + defer func() { + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelManagement = restrictPublicChannel }) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManagement = restrictPrivateChannel }) + utils.SetIsLicensed(isLicensed) + utils.SetLicense(license) + th.App.SetDefaultRolesBasedOnConfig() + }() th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_ALL }) th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_ALL }) - th.App.SetLicense(model.NewTestLicense()) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + th.App.SetDefaultRolesBasedOnConfig() Client = th.Client team = th.BasicTeam @@ -889,8 +926,11 @@ func TestDeleteChannel(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelDeletion = model.PERMISSIONS_CHANNEL_ADMIN + }) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelDeletion = model.PERMISSIONS_CHANNEL_ADMIN }) + th.App.SetDefaultRolesBasedOnConfig() // channels created by SystemAdmin publicChannel6 = th.CreateChannelWithClient(th.SystemAdminClient, model.CHANNEL_OPEN) @@ -927,7 +967,9 @@ func TestDeleteChannel(t *testing.T) { // successful delete by team admin th.UpdateUserToTeamAdmin(user, team) th.App.InvalidateAllCaches() - th.App.SetLicense(model.NewTestLicense()) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() _, resp = Client.DeleteChannel(publicChannel6.Id) CheckNoError(t, resp) @@ -937,11 +979,16 @@ func TestDeleteChannel(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelDeletion = model.PERMISSIONS_TEAM_ADMIN + }) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelDeletion = model.PERMISSIONS_TEAM_ADMIN }) + th.App.SetDefaultRolesBasedOnConfig() th.UpdateUserToNonTeamAdmin(user, team) th.App.InvalidateAllCaches() - th.App.SetLicense(model.NewTestLicense()) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() // channels created by SystemAdmin publicChannel6 = th.CreateChannelWithClient(th.SystemAdminClient, model.CHANNEL_OPEN) @@ -971,7 +1018,9 @@ func TestDeleteChannel(t *testing.T) { // successful delete by team admin th.UpdateUserToTeamAdmin(th.BasicUser, team) th.App.InvalidateAllCaches() - th.App.SetLicense(model.NewTestLicense()) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() _, resp = Client.DeleteChannel(publicChannel6.Id) CheckNoError(t, resp) @@ -981,8 +1030,11 @@ func TestDeleteChannel(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPublicChannelDeletion = model.PERMISSIONS_SYSTEM_ADMIN + }) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelDeletion = model.PERMISSIONS_SYSTEM_ADMIN }) + th.App.SetDefaultRolesBasedOnConfig() // channels created by SystemAdmin publicChannel6 = th.CreateChannelWithClient(th.SystemAdminClient, model.CHANNEL_OPEN) @@ -1012,7 +1064,9 @@ func TestDeleteChannel(t *testing.T) { // cannot delete by team admin th.UpdateUserToTeamAdmin(th.BasicUser, team) th.App.InvalidateAllCaches() - th.App.SetLicense(model.NewTestLicense()) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() _, resp = Client.DeleteChannel(publicChannel6.Id) CheckForbiddenStatus(t, resp) @@ -1769,9 +1823,16 @@ func TestAddChannelMember(t *testing.T) { CheckNoError(t, resp) // Test policy does not apply to TE. + restrictPrivateChannel := *th.App.Config().TeamSettings.RestrictPrivateChannelManageMembers + defer func() { + th.App.UpdateConfig(func(cfg *model.Config) { + *cfg.TeamSettings.RestrictPrivateChannelManageMembers = restrictPrivateChannel + }) + }() th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_CHANNEL_ADMIN }) + th.App.SetDefaultRolesBasedOnConfig() Client.Login(user2.Username, user2.Password) privateChannel = th.CreatePrivateChannel() @@ -1785,8 +1846,18 @@ func TestAddChannelMember(t *testing.T) { Client.Logout() // Add a license + isLicensed := utils.IsLicensed() + license := utils.License() + defer func() { + utils.SetIsLicensed(isLicensed) + utils.SetLicense(license) + th.App.SetDefaultRolesBasedOnConfig() + }() th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_ALL }) - th.App.SetLicense(model.NewTestLicense()) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + th.App.SetDefaultRolesBasedOnConfig() // Check that a regular channel user can add other users. Client.Login(user2.Username, user2.Password) @@ -1804,6 +1875,10 @@ func TestAddChannelMember(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_CHANNEL_ADMIN }) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + th.App.SetDefaultRolesBasedOnConfig() Client.Login(user2.Username, user2.Password) privateChannel = th.CreatePrivateChannel() @@ -1818,7 +1893,10 @@ func TestAddChannelMember(t *testing.T) { th.MakeUserChannelAdmin(user, privateChannel) th.App.InvalidateAllCaches() - th.App.SetLicense(model.NewTestLicense()) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + th.App.SetDefaultRolesBasedOnConfig() Client.Login(user.Username, user.Password) _, resp = Client.AddChannelMember(privateChannel.Id, user3.Id) @@ -1829,6 +1907,10 @@ func TestAddChannelMember(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_TEAM_ADMIN }) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + th.App.SetDefaultRolesBasedOnConfig() Client.Login(user2.Username, user2.Password) privateChannel = th.CreatePrivateChannel() @@ -1843,7 +1925,10 @@ func TestAddChannelMember(t *testing.T) { th.UpdateUserToTeamAdmin(user, team) th.App.InvalidateAllCaches() - th.App.SetLicense(model.NewTestLicense()) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + th.App.SetDefaultRolesBasedOnConfig() Client.Login(user.Username, user.Password) _, resp = Client.AddChannelMember(privateChannel.Id, user3.Id) @@ -1854,6 +1939,10 @@ func TestAddChannelMember(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_SYSTEM_ADMIN }) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + th.App.SetDefaultRolesBasedOnConfig() Client.Login(user2.Username, user2.Password) privateChannel = th.CreatePrivateChannel() @@ -1930,9 +2019,16 @@ func TestRemoveChannelMember(t *testing.T) { th.App.InvalidateAllCaches() // Test policy does not apply to TE. + restrictPrivateChannel := *th.App.Config().TeamSettings.RestrictPrivateChannelManageMembers + defer func() { + th.App.UpdateConfig(func(cfg *model.Config) { + *cfg.TeamSettings.RestrictPrivateChannelManageMembers = restrictPrivateChannel + }) + }() th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_CHANNEL_ADMIN }) + th.App.SetDefaultRolesBasedOnConfig() privateChannel := th.CreateChannelWithClient(th.SystemAdminClient, model.CHANNEL_PRIVATE) _, resp = th.SystemAdminClient.AddChannelMember(privateChannel.Id, user1.Id) @@ -1944,8 +2040,18 @@ func TestRemoveChannelMember(t *testing.T) { CheckNoError(t, resp) // Add a license + isLicensed := utils.IsLicensed() + license := utils.License() + defer func() { + utils.SetIsLicensed(isLicensed) + utils.SetLicense(license) + th.App.SetDefaultRolesBasedOnConfig() + }() th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_ALL }) - th.App.SetLicense(model.NewTestLicense()) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + th.App.SetDefaultRolesBasedOnConfig() // Check that a regular channel user can remove other users. privateChannel = th.CreateChannelWithClient(th.SystemAdminClient, model.CHANNEL_PRIVATE) @@ -1961,6 +2067,10 @@ func TestRemoveChannelMember(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_CHANNEL_ADMIN }) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + th.App.SetDefaultRolesBasedOnConfig() privateChannel = th.CreateChannelWithClient(th.SystemAdminClient, model.CHANNEL_PRIVATE) _, resp = th.SystemAdminClient.AddChannelMember(privateChannel.Id, user1.Id) @@ -1973,7 +2083,9 @@ func TestRemoveChannelMember(t *testing.T) { th.MakeUserChannelAdmin(user1, privateChannel) th.App.InvalidateAllCaches() - th.App.SetLicense(model.NewTestLicense()) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() _, resp = Client.RemoveUserFromChannel(privateChannel.Id, user2.Id) CheckNoError(t, resp) @@ -1982,6 +2094,10 @@ func TestRemoveChannelMember(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_TEAM_ADMIN }) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + th.App.SetDefaultRolesBasedOnConfig() privateChannel = th.CreateChannelWithClient(th.SystemAdminClient, model.CHANNEL_PRIVATE) _, resp = th.SystemAdminClient.AddChannelMember(privateChannel.Id, user1.Id) @@ -1994,7 +2110,9 @@ func TestRemoveChannelMember(t *testing.T) { th.UpdateUserToTeamAdmin(user1, team) th.App.InvalidateAllCaches() - th.App.SetLicense(model.NewTestLicense()) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() _, resp = Client.RemoveUserFromChannel(privateChannel.Id, user2.Id) CheckNoError(t, resp) @@ -2003,6 +2121,10 @@ func TestRemoveChannelMember(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictPrivateChannelManageMembers = model.PERMISSIONS_SYSTEM_ADMIN }) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + th.App.SetDefaultRolesBasedOnConfig() privateChannel = th.CreateChannelWithClient(th.SystemAdminClient, model.CHANNEL_PRIVATE) _, resp = th.SystemAdminClient.AddChannelMember(privateChannel.Id, user1.Id) diff --git a/api4/file.go b/api4/file.go index acc4c78e5..48ee281fe 100644 --- a/api4/file.go +++ b/api4/file.go @@ -281,13 +281,13 @@ func getPublicFile(c *Context, w http.ResponseWriter, r *http.Request) { if len(hash) == 0 { c.Err = model.NewAppError("getPublicFile", "api.file.get_file.public_invalid.app_error", nil, "", http.StatusBadRequest) - utils.RenderWebAppError(w, r, c.Err, c.App.AsymmetricSigningKey()) + http.Redirect(w, r, c.GetSiteURLHeader()+"/error?message="+utils.T(c.Err.Message), http.StatusTemporaryRedirect) return } if hash != app.GeneratePublicLinkHash(info.Id, *c.App.Config().FileSettings.PublicLinkSalt) { c.Err = model.NewAppError("getPublicFile", "api.file.get_file.public_invalid.app_error", nil, "", http.StatusBadRequest) - utils.RenderWebAppError(w, r, c.Err, c.App.AsymmetricSigningKey()) + http.Redirect(w, r, c.GetSiteURLHeader()+"/error?message="+utils.T(c.Err.Message), http.StatusTemporaryRedirect) return } diff --git a/api4/oauth.go b/api4/oauth.go index d0f43256a..655adaaee 100644 --- a/api4/oauth.go +++ b/api4/oauth.go @@ -313,7 +313,7 @@ func deauthorizeOAuthApp(c *Context, w http.ResponseWriter, r *http.Request) { func authorizeOAuthPage(c *Context, w http.ResponseWriter, r *http.Request) { if !c.App.Config().ServiceSettings.EnableOAuthServiceProvider { err := model.NewAppError("authorizeOAuth", "api.oauth.authorize_oauth.disabled.app_error", nil, "", http.StatusNotImplemented) - utils.RenderWebAppError(w, r, err, c.App.AsymmetricSigningKey()) + utils.RenderWebError(err, w, r) return } @@ -326,13 +326,13 @@ func authorizeOAuthPage(c *Context, w http.ResponseWriter, r *http.Request) { } if err := authRequest.IsValid(); err != nil { - utils.RenderWebAppError(w, r, err, c.App.AsymmetricSigningKey()) + utils.RenderWebError(err, w, r) return } oauthApp, err := c.App.GetOAuthApp(authRequest.ClientId) if err != nil { - utils.RenderWebAppError(w, r, err, c.App.AsymmetricSigningKey()) + utils.RenderWebError(err, w, r) return } @@ -343,8 +343,7 @@ func authorizeOAuthPage(c *Context, w http.ResponseWriter, r *http.Request) { } if !oauthApp.IsValidRedirectURL(authRequest.RedirectUri) { - err := model.NewAppError("authorizeOAuthPage", "api.oauth.allow_oauth.redirect_callback.app_error", nil, "", http.StatusBadRequest) - utils.RenderWebAppError(w, r, err, c.App.AsymmetricSigningKey()) + utils.RenderWebError(model.NewAppError("authorizeOAuthPage", "api.oauth.allow_oauth.redirect_callback.app_error", nil, "", http.StatusBadRequest), w, r) return } @@ -361,7 +360,7 @@ func authorizeOAuthPage(c *Context, w http.ResponseWriter, r *http.Request) { redirectUrl, err := c.App.AllowOAuthAppAccessToUser(c.Session.UserId, authRequest) if err != nil { - utils.RenderWebAppError(w, r, err, c.App.AsymmetricSigningKey()) + utils.RenderWebError(err, w, r) return } @@ -442,10 +441,7 @@ func completeOAuth(c *Context, w http.ResponseWriter, r *http.Request) { code := r.URL.Query().Get("code") if len(code) == 0 { - utils.RenderWebError(w, r, http.StatusTemporaryRedirect, url.Values{ - "type": []string{"oauth_missing_code"}, - "service": []string{strings.Title(service)}, - }, c.App.AsymmetricSigningKey()) + http.Redirect(w, r, c.GetSiteURLHeader()+"/error?type=oauth_missing_code&service="+strings.Title(service), http.StatusTemporaryRedirect) return } @@ -466,7 +462,7 @@ func completeOAuth(c *Context, w http.ResponseWriter, r *http.Request) { if action == model.OAUTH_ACTION_MOBILE { w.Write([]byte(err.ToJson())) } else { - utils.RenderWebAppError(w, r, err, c.App.AsymmetricSigningKey()) + http.Redirect(w, r, c.GetSiteURLHeader()+"/error?message="+url.QueryEscape(err.Message), http.StatusTemporaryRedirect) } return } @@ -478,7 +474,7 @@ func completeOAuth(c *Context, w http.ResponseWriter, r *http.Request) { if action == model.OAUTH_ACTION_MOBILE { w.Write([]byte(err.ToJson())) } else { - utils.RenderWebAppError(w, r, err, c.App.AsymmetricSigningKey()) + http.Redirect(w, r, c.GetSiteURLHeader()+"/error?message="+url.QueryEscape(err.Message), http.StatusTemporaryRedirect) } return } @@ -563,9 +559,7 @@ func signupWithOAuth(c *Context, w http.ResponseWriter, r *http.Request) { } if !c.App.Config().TeamSettings.EnableUserCreation { - utils.RenderWebError(w, r, http.StatusBadRequest, url.Values{ - "message": []string{utils.T("api.oauth.singup_with_oauth.disabled.app_error")}, - }, c.App.AsymmetricSigningKey()) + http.Redirect(w, r, c.GetSiteURLHeader()+"/error?message="+url.QueryEscape(utils.T("api.oauth.singup_with_oauth.disabled.app_error")), http.StatusTemporaryRedirect) return } diff --git a/api4/oauth_test.go b/api4/oauth_test.go index c871dafff..8dd602456 100644 --- a/api4/oauth_test.go +++ b/api4/oauth_test.go @@ -18,7 +18,14 @@ func TestCreateOAuthApp(t *testing.T) { Client := th.Client AdminClient := th.SystemAdminClient + enableOAuth := th.App.Config().ServiceSettings.EnableOAuthServiceProvider + adminOnly := *th.App.Config().ServiceSettings.EnableOnlyAdminIntegrations + defer func() { + th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = enableOAuth }) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = adminOnly }) + }() th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = true }) + th.App.SetDefaultRolesBasedOnConfig() oapp := &model.OAuthApp{Name: GenerateTestAppName(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}, IsTrusted: true} @@ -35,10 +42,12 @@ func TestCreateOAuthApp(t *testing.T) { } th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true }) + th.App.SetDefaultRolesBasedOnConfig() _, resp = Client.CreateOAuthApp(oapp) CheckForbiddenStatus(t, resp) th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) + th.App.SetDefaultRolesBasedOnConfig() rapp, resp = Client.CreateOAuthApp(oapp) CheckNoError(t, resp) CheckCreatedStatus(t, resp) @@ -77,7 +86,14 @@ func TestUpdateOAuthApp(t *testing.T) { Client := th.Client AdminClient := th.SystemAdminClient + enableOAuth := th.App.Config().ServiceSettings.EnableOAuthServiceProvider + adminOnly := *th.App.Config().ServiceSettings.EnableOnlyAdminIntegrations + defer func() { + th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = enableOAuth }) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = adminOnly }) + }() th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = true }) + th.App.SetDefaultRolesBasedOnConfig() oapp := &model.OAuthApp{ Name: "oapp", @@ -156,6 +172,7 @@ func TestUpdateOAuthApp(t *testing.T) { th.LoginBasic() th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) + th.App.SetDefaultRolesBasedOnConfig() _, resp = Client.UpdateOAuthApp(oapp) CheckForbiddenStatus(t, resp) @@ -182,8 +199,15 @@ func TestGetOAuthApps(t *testing.T) { Client := th.Client AdminClient := th.SystemAdminClient + enableOAuth := th.App.Config().ServiceSettings.EnableOAuthServiceProvider + adminOnly := *th.App.Config().ServiceSettings.EnableOnlyAdminIntegrations + defer func() { + th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = enableOAuth }) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = adminOnly }) + }() th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = true }) th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) + th.App.SetDefaultRolesBasedOnConfig() oapp := &model.OAuthApp{Name: GenerateTestAppName(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}} @@ -227,6 +251,7 @@ func TestGetOAuthApps(t *testing.T) { } th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true }) + th.App.SetDefaultRolesBasedOnConfig() _, resp = Client.GetOAuthApps(0, 1000) CheckForbiddenStatus(t, resp) @@ -247,8 +272,15 @@ func TestGetOAuthApp(t *testing.T) { Client := th.Client AdminClient := th.SystemAdminClient + enableOAuth := th.App.Config().ServiceSettings.EnableOAuthServiceProvider + adminOnly := *th.App.Config().ServiceSettings.EnableOnlyAdminIntegrations + defer func() { + th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = enableOAuth }) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = adminOnly }) + }() th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = true }) th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) + th.App.SetDefaultRolesBasedOnConfig() oapp := &model.OAuthApp{Name: GenerateTestAppName(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}} @@ -288,6 +320,7 @@ func TestGetOAuthApp(t *testing.T) { CheckForbiddenStatus(t, resp) th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true }) + th.App.SetDefaultRolesBasedOnConfig() _, resp = Client.GetOAuthApp(rapp2.Id) CheckForbiddenStatus(t, resp) @@ -314,8 +347,15 @@ func TestGetOAuthAppInfo(t *testing.T) { Client := th.Client AdminClient := th.SystemAdminClient + enableOAuth := th.App.Config().ServiceSettings.EnableOAuthServiceProvider + adminOnly := *th.App.Config().ServiceSettings.EnableOnlyAdminIntegrations + defer func() { + th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = enableOAuth }) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = adminOnly }) + }() th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = true }) th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) + th.App.SetDefaultRolesBasedOnConfig() oapp := &model.OAuthApp{Name: GenerateTestAppName(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}} @@ -355,6 +395,7 @@ func TestGetOAuthAppInfo(t *testing.T) { CheckNoError(t, resp) th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true }) + th.App.SetDefaultRolesBasedOnConfig() _, resp = Client.GetOAuthAppInfo(rapp2.Id) CheckNoError(t, resp) @@ -381,8 +422,15 @@ func TestDeleteOAuthApp(t *testing.T) { Client := th.Client AdminClient := th.SystemAdminClient + enableOAuth := th.App.Config().ServiceSettings.EnableOAuthServiceProvider + adminOnly := *th.App.Config().ServiceSettings.EnableOnlyAdminIntegrations + defer func() { + th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = enableOAuth }) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = adminOnly }) + }() th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = true }) th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) + th.App.SetDefaultRolesBasedOnConfig() oapp := &model.OAuthApp{Name: GenerateTestAppName(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}} @@ -417,6 +465,7 @@ func TestDeleteOAuthApp(t *testing.T) { CheckNoError(t, resp) th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) + th.App.SetDefaultRolesBasedOnConfig() _, resp = Client.DeleteOAuthApp(rapp.Id) CheckForbiddenStatus(t, resp) @@ -441,8 +490,15 @@ func TestRegenerateOAuthAppSecret(t *testing.T) { Client := th.Client AdminClient := th.SystemAdminClient + enableOAuth := th.App.Config().ServiceSettings.EnableOAuthServiceProvider + adminOnly := *th.App.Config().ServiceSettings.EnableOnlyAdminIntegrations + defer func() { + th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = enableOAuth }) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = adminOnly }) + }() th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = true }) th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) + th.App.SetDefaultRolesBasedOnConfig() oapp := &model.OAuthApp{Name: GenerateTestAppName(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}} @@ -481,6 +537,7 @@ func TestRegenerateOAuthAppSecret(t *testing.T) { CheckNoError(t, resp) th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = false }) + th.App.SetDefaultRolesBasedOnConfig() _, resp = Client.RegenerateOAuthAppSecret(rapp.Id) CheckForbiddenStatus(t, resp) @@ -565,7 +622,12 @@ func TestAuthorizeOAuthApp(t *testing.T) { Client := th.Client AdminClient := th.SystemAdminClient + enableOAuth := th.App.Config().ServiceSettings.EnableOAuthServiceProvider + defer func() { + th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = enableOAuth }) + }() th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = true }) + th.App.SetDefaultRolesBasedOnConfig() oapp := &model.OAuthApp{Name: GenerateTestAppName(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}} diff --git a/api4/post_test.go b/api4/post_test.go index 257918525..6f770b70a 100644 --- a/api4/post_test.go +++ b/api4/post_test.go @@ -17,6 +17,7 @@ import ( "github.com/mattermost/mattermost-server/app" "github.com/mattermost/mattermost-server/model" + "github.com/mattermost/mattermost-server/utils" ) func TestCreatePost(t *testing.T) { @@ -129,8 +130,20 @@ func testCreatePostWithOutgoingHook( team := th.BasicTeam channel := th.BasicChannel + enableOutgoingHooks := th.App.Config().ServiceSettings.EnableOutgoingWebhooks + enableAdminOnlyHooks := th.App.Config().ServiceSettings.EnableOnlyAdminIntegrations + allowedInternalConnections := *th.App.Config().ServiceSettings.AllowedUntrustedInternalConnections + defer func() { + th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOutgoingWebhooks = enableOutgoingHooks }) + th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOnlyAdminIntegrations = enableAdminOnlyHooks }) + th.App.SetDefaultRolesBasedOnConfig() + th.App.UpdateConfig(func(cfg *model.Config) { + cfg.ServiceSettings.AllowedUntrustedInternalConnections = &allowedInternalConnections + }) + }() th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOutgoingWebhooks = true }) th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOnlyAdminIntegrations = true }) + th.App.SetDefaultRolesBasedOnConfig() th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.AllowedUntrustedInternalConnections = "localhost 127.0.0.1" }) @@ -476,8 +489,21 @@ func TestUpdatePost(t *testing.T) { Client := th.Client channel := th.BasicChannel - th.App.SetLicense(model.NewTestLicense()) + isLicensed := utils.IsLicensed() + license := utils.License() + allowEditPost := *th.App.Config().ServiceSettings.AllowEditPost + defer func() { + utils.SetIsLicensed(isLicensed) + utils.SetLicense(license) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.AllowEditPost = allowEditPost }) + th.App.SetDefaultRolesBasedOnConfig() + }() + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.AllowEditPost = model.ALLOW_EDIT_POST_ALWAYS }) + th.App.SetDefaultRolesBasedOnConfig() post := &model.Post{ChannelId: channel.Id, Message: "zz" + model.NewId() + "a"} rpost, resp := Client.CreatePost(post) @@ -548,8 +574,21 @@ func TestPatchPost(t *testing.T) { Client := th.Client channel := th.BasicChannel - th.App.SetLicense(model.NewTestLicense()) + isLicensed := utils.IsLicensed() + license := utils.License() + allowEditPost := *th.App.Config().ServiceSettings.AllowEditPost + defer func() { + utils.SetIsLicensed(isLicensed) + utils.SetLicense(license) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.AllowEditPost = allowEditPost }) + th.App.SetDefaultRolesBasedOnConfig() + }() + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.AllowEditPost = model.ALLOW_EDIT_POST_ALWAYS }) + th.App.SetDefaultRolesBasedOnConfig() post := &model.Post{ ChannelId: channel.Id, diff --git a/api4/system.go b/api4/system.go index 061ffe094..43b941247 100644 --- a/api4/system.go +++ b/api4/system.go @@ -266,7 +266,7 @@ func getClientLicense(c *Context, w http.ResponseWriter, r *http.Request) { return } - etag := c.App.GetClientLicenseEtag(true) + etag := utils.GetClientLicenseEtag(true) if c.HandleEtag(etag, "Get Client License", w, r) { return } @@ -274,9 +274,9 @@ func getClientLicense(c *Context, w http.ResponseWriter, r *http.Request) { var clientLicense map[string]string if c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) { - clientLicense = c.App.ClientLicense() + clientLicense = utils.ClientLicense() } else { - clientLicense = c.App.GetSanitizedClientLicense() + clientLicense = utils.GetSanitizedClientLicense() } w.Header().Set(model.HEADER_ETAG_SERVER, etag) diff --git a/api4/team_test.go b/api4/team_test.go index faa90e511..d365fd686 100644 --- a/api4/team_test.go +++ b/api4/team_test.go @@ -68,7 +68,13 @@ func TestCreateTeam(t *testing.T) { CheckUnauthorizedStatus(t, resp) // Update permission + enableTeamCreation := th.App.Config().TeamSettings.EnableTeamCreation + defer func() { + th.App.UpdateConfig(func(cfg *model.Config) { cfg.TeamSettings.EnableTeamCreation = enableTeamCreation }) + th.App.SetDefaultRolesBasedOnConfig() + }() th.App.UpdateConfig(func(cfg *model.Config) { cfg.TeamSettings.EnableTeamCreation = false }) + th.App.SetDefaultRolesBasedOnConfig() th.LoginBasic() _, resp = Client.CreateTeam(team) @@ -1286,8 +1292,20 @@ func TestAddTeamMember(t *testing.T) { Client.Logout() + // Check effects of config and license changes. + restrictTeamInvite := *th.App.Config().TeamSettings.RestrictTeamInvite + isLicensed := utils.IsLicensed() + license := utils.License() + defer func() { + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictTeamInvite = restrictTeamInvite }) + utils.SetIsLicensed(isLicensed) + utils.SetLicense(license) + th.App.SetDefaultRolesBasedOnConfig() + }() + // Set the config so that only team admins can add a user to a team. th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictTeamInvite = model.PERMISSIONS_TEAM_ADMIN }) + th.App.SetDefaultRolesBasedOnConfig() th.LoginBasic() // Test without the EE license to see that the permission restriction is ignored. @@ -1295,7 +1313,10 @@ func TestAddTeamMember(t *testing.T) { CheckNoError(t, resp) // Add an EE license. - th.App.SetLicense(model.NewTestLicense()) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + th.App.SetDefaultRolesBasedOnConfig() th.LoginBasic() // Check that a regular user can't add someone to the team. @@ -1306,7 +1327,10 @@ func TestAddTeamMember(t *testing.T) { th.UpdateUserToTeamAdmin(th.BasicUser, th.BasicTeam) th.App.InvalidateAllCaches() th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictTeamInvite = model.PERMISSIONS_TEAM_ADMIN }) - th.App.SetLicense(model.NewTestLicense()) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + th.App.SetDefaultRolesBasedOnConfig() th.LoginBasic() // Should work as a team admin. @@ -1315,6 +1339,7 @@ func TestAddTeamMember(t *testing.T) { // Change permission level to System Admin th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictTeamInvite = model.PERMISSIONS_SYSTEM_ADMIN }) + th.App.SetDefaultRolesBasedOnConfig() // Should not work as team admin. _, resp = Client.AddTeamMember(team.Id, otherUser.Id) @@ -1328,13 +1353,21 @@ func TestAddTeamMember(t *testing.T) { th.UpdateUserToNonTeamAdmin(th.BasicUser, th.BasicTeam) th.App.InvalidateAllCaches() th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictTeamInvite = model.PERMISSIONS_ALL }) - th.App.SetLicense(model.NewTestLicense()) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + th.App.SetDefaultRolesBasedOnConfig() th.LoginBasic() // Should work as a regular user. _, resp = Client.AddTeamMember(team.Id, otherUser.Id) CheckNoError(t, resp) + // Reset config and license. + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictTeamInvite = restrictTeamInvite }) + utils.SetIsLicensed(isLicensed) + utils.SetLicense(license) + th.App.SetDefaultRolesBasedOnConfig() th.LoginBasic() // by hash and data @@ -1474,8 +1507,20 @@ func TestAddTeamMembers(t *testing.T) { Client.Logout() + // Check effects of config and license changes. + restrictTeamInvite := *th.App.Config().TeamSettings.RestrictTeamInvite + isLicensed := utils.IsLicensed() + license := utils.License() + defer func() { + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictTeamInvite = restrictTeamInvite }) + utils.SetIsLicensed(isLicensed) + utils.SetLicense(license) + th.App.SetDefaultRolesBasedOnConfig() + }() + // Set the config so that only team admins can add a user to a team. th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictTeamInvite = model.PERMISSIONS_TEAM_ADMIN }) + th.App.SetDefaultRolesBasedOnConfig() th.LoginBasic() // Test without the EE license to see that the permission restriction is ignored. @@ -1483,7 +1528,10 @@ func TestAddTeamMembers(t *testing.T) { CheckNoError(t, resp) // Add an EE license. - th.App.SetLicense(model.NewTestLicense()) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + th.App.SetDefaultRolesBasedOnConfig() th.LoginBasic() // Check that a regular user can't add someone to the team. @@ -1494,7 +1542,10 @@ func TestAddTeamMembers(t *testing.T) { th.UpdateUserToTeamAdmin(th.BasicUser, th.BasicTeam) th.App.InvalidateAllCaches() th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictTeamInvite = model.PERMISSIONS_TEAM_ADMIN }) - th.App.SetLicense(model.NewTestLicense()) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + th.App.SetDefaultRolesBasedOnConfig() th.LoginBasic() // Should work as a team admin. @@ -1503,6 +1554,7 @@ func TestAddTeamMembers(t *testing.T) { // Change permission level to System Admin th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictTeamInvite = model.PERMISSIONS_SYSTEM_ADMIN }) + th.App.SetDefaultRolesBasedOnConfig() // Should not work as team admin. _, resp = Client.AddTeamMembers(team.Id, userList) @@ -1516,7 +1568,10 @@ func TestAddTeamMembers(t *testing.T) { th.UpdateUserToNonTeamAdmin(th.BasicUser, th.BasicTeam) th.App.InvalidateAllCaches() th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictTeamInvite = model.PERMISSIONS_ALL }) - th.App.SetLicense(model.NewTestLicense()) + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + th.App.SetDefaultRolesBasedOnConfig() th.LoginBasic() // Should work as a regular user. @@ -1875,6 +1930,10 @@ func TestInviteUsersToTeam(t *testing.T) { } } + restrictCreationToDomains := th.App.Config().TeamSettings.RestrictCreationToDomains + defer func() { + th.App.UpdateConfig(func(cfg *model.Config) { cfg.TeamSettings.RestrictCreationToDomains = restrictCreationToDomains }) + }() th.App.UpdateConfig(func(cfg *model.Config) { cfg.TeamSettings.RestrictCreationToDomains = "@example.com" }) err := th.App.InviteNewUsersToTeam(emailList, th.BasicTeam.Id, th.BasicUser.Id) diff --git a/api4/user_test.go b/api4/user_test.go index 4613a8ea9..a7b7d297d 100644 --- a/api4/user_test.go +++ b/api4/user_test.go @@ -1566,7 +1566,18 @@ func TestUpdateUserMfa(t *testing.T) { defer th.TearDown() Client := th.Client - th.App.SetLicense(model.NewTestLicense("mfa")) + isLicensed := utils.IsLicensed() + license := utils.License() + enableMfa := *th.App.Config().ServiceSettings.EnableMultifactorAuthentication + defer func() { + utils.SetIsLicensed(isLicensed) + utils.SetLicense(license) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableMultifactorAuthentication = enableMfa }) + }() + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + *utils.License().Features.MFA = true th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableMultifactorAuthentication = true }) session, _ := th.App.GetSession(Client.AuthToken) @@ -1601,7 +1612,18 @@ func TestCheckUserMfa(t *testing.T) { t.Fatal("should be false - mfa not active") } - th.App.SetLicense(model.NewTestLicense("mfa")) + isLicensed := utils.IsLicensed() + license := utils.License() + enableMfa := *th.App.Config().ServiceSettings.EnableMultifactorAuthentication + defer func() { + utils.SetIsLicensed(isLicensed) + utils.SetLicense(license) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableMultifactorAuthentication = enableMfa }) + }() + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + *utils.License().Features.MFA = true th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableMultifactorAuthentication = true }) th.LoginBasic() @@ -1637,7 +1659,18 @@ func TestGenerateMfaSecret(t *testing.T) { _, resp = Client.GenerateMfaSecret("junk") CheckBadRequestStatus(t, resp) - th.App.SetLicense(model.NewTestLicense("mfa")) + isLicensed := utils.IsLicensed() + license := utils.License() + enableMfa := *th.App.Config().ServiceSettings.EnableMultifactorAuthentication + defer func() { + utils.SetIsLicensed(isLicensed) + utils.SetLicense(license) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableMultifactorAuthentication = enableMfa }) + }() + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + *utils.License().Features.MFA = true th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableMultifactorAuthentication = true }) _, resp = Client.GenerateMfaSecret(model.NewId()) @@ -2154,7 +2187,19 @@ func TestSwitchAccount(t *testing.T) { t.Fatal("bad link") } - th.App.SetLicense(model.NewTestLicense()) + isLicensed := utils.IsLicensed() + license := utils.License() + enableAuthenticationTransfer := *th.App.Config().ServiceSettings.ExperimentalEnableAuthenticationTransfer + defer func() { + utils.SetIsLicensed(isLicensed) + utils.SetLicense(license) + th.App.UpdateConfig(func(cfg *model.Config) { + *cfg.ServiceSettings.ExperimentalEnableAuthenticationTransfer = enableAuthenticationTransfer + }) + }() + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ExperimentalEnableAuthenticationTransfer = false }) sr = &model.SwitchRequest{ diff --git a/app/admin.go b/app/admin.go index 154fa8899..b838ed3bd 100644 --- a/app/admin.go +++ b/app/admin.go @@ -237,8 +237,7 @@ func (a *App) TestEmail(userId string, cfg *model.Config) *model.AppError { return err } else { T := utils.GetUserTranslations(user.Locale) - license := a.License() - if err := utils.SendMailUsingConfig(user.Email, T("api.admin.test_email.subject"), T("api.admin.test_email.body"), cfg, license != nil && *license.Features.Compliance); err != nil { + if err := utils.SendMailUsingConfig(user.Email, T("api.admin.test_email.subject"), T("api.admin.test_email.body"), cfg); err != nil { return err } } diff --git a/app/app.go b/app/app.go index 636f0a428..1e46d29d0 100644 --- a/app/app.go +++ b/app/app.go @@ -4,7 +4,6 @@ package app import ( - "crypto/ecdsa" "html/template" "net" "net/http" @@ -59,20 +58,15 @@ type App struct { configFile string configListeners map[string]func(*model.Config, *model.Config) - licenseValue atomic.Value - clientLicenseValue atomic.Value - licenseListeners map[string]func() - newStore func() store.Store - htmlTemplateWatcher *utils.HTMLTemplateWatcher - sessionCache *utils.Cache - roles map[string]*model.Role - configListenerId string - licenseListenerId string - disableConfigWatch bool - configWatcher *utils.ConfigWatcher - asymmetricSigningKey *ecdsa.PrivateKey + htmlTemplateWatcher *utils.HTMLTemplateWatcher + sessionCache *utils.Cache + roles map[string]*model.Role + configListenerId string + licenseListenerId string + disableConfigWatch bool + configWatcher *utils.ConfigWatcher pluginCommands []*PluginCommand pluginCommandsLock sync.RWMutex @@ -86,7 +80,7 @@ var appCount = 0 // New creates a new App. You must call Shutdown when you're done with it. // XXX: For now, only one at a time is allowed as some resources are still shared. -func New(options ...Option) (outApp *App, outErr error) { +func New(options ...Option) (*App, error) { appCount++ if appCount > 1 { panic("Only one App should exist at a time. Did you forget to call Shutdown()?") @@ -97,17 +91,11 @@ func New(options ...Option) (outApp *App, outErr error) { Srv: &Server{ Router: mux.NewRouter(), }, - sessionCache: utils.NewLru(model.SESSION_CACHE_SIZE), - configFile: "config.json", - configListeners: make(map[string]func(*model.Config, *model.Config)), - clientConfig: make(map[string]string), - licenseListeners: map[string]func(){}, - } - defer func() { - if outErr != nil { - app.Shutdown() - } - }() + sessionCache: utils.NewLru(model.SESSION_CACHE_SIZE), + configFile: "config.json", + configListeners: make(map[string]func(*model.Config, *model.Config)), + clientConfig: make(map[string]string), + } for _, option := range options { option(app) @@ -130,9 +118,9 @@ func New(options ...Option) (outApp *App, outErr error) { app.configListenerId = app.AddConfigListener(func(_, _ *model.Config) { app.configOrLicenseListener() }) - app.licenseListenerId = app.AddLicenseListener(app.configOrLicenseListener) + app.licenseListenerId = utils.AddLicenseListener(app.configOrLicenseListener) app.regenerateClientConfig() - app.setDefaultRolesBasedOnConfig() + app.SetDefaultRolesBasedOnConfig() l4g.Info(utils.T("api.server.new_server.init.info")) @@ -151,10 +139,6 @@ func New(options ...Option) (outApp *App, outErr error) { } app.Srv.Store = app.newStore() - if err := app.ensureAsymmetricSigningKey(); err != nil { - return nil, errors.Wrapf(err, "unable to ensure asymmetric signing key") - } - app.initJobs() app.initBuiltInPlugins() @@ -173,7 +157,7 @@ func New(options ...Option) (outApp *App, outErr error) { func (a *App) configOrLicenseListener() { a.regenerateClientConfig() - a.setDefaultRolesBasedOnConfig() + a.SetDefaultRolesBasedOnConfig() } func (a *App) Shutdown() { @@ -187,9 +171,7 @@ func (a *App) Shutdown() { a.ShutDownPlugins() a.WaitForGoroutines() - if a.Srv.Store != nil { - a.Srv.Store.Close() - } + a.Srv.Store.Close() a.Srv = nil if a.htmlTemplateWatcher != nil { @@ -197,7 +179,7 @@ func (a *App) Shutdown() { } a.RemoveConfigListener(a.configListenerId) - a.RemoveLicenseListener(a.licenseListenerId) + utils.RemoveLicenseListener(a.licenseListenerId) l4g.Info(utils.T("api.server.stop_server.stopped.info")) a.DisableConfigWatch() @@ -466,5 +448,5 @@ func (a *App) Handle404(w http.ResponseWriter, r *http.Request) { l4g.Debug("%v: code=404 ip=%v", r.URL.Path, utils.GetIpAddress(r)) - utils.RenderWebAppError(w, r, err, a.AsymmetricSigningKey()) + utils.RenderWebError(err, w, r) } diff --git a/app/app_test.go b/app/app_test.go index 09f8725d7..25b19ead8 100644 --- a/app/app_test.go +++ b/app/app_test.go @@ -51,8 +51,7 @@ func TestAppRace(t *testing.T) { a, err := New() require.NoError(t, err) a.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = ":0" }) - serverErr := a.StartServer() - require.NoError(t, serverErr) + a.StartServer() a.Shutdown() } } diff --git a/app/apptestlib.go b/app/apptestlib.go index c7846c9b5..09afc8f76 100644 --- a/app/apptestlib.go +++ b/app/apptestlib.go @@ -96,20 +96,15 @@ func setupTestHelper(enterprise bool) *TestHelper { if testStore != nil { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = ":0" }) } - serverErr := th.App.StartServer() - if serverErr != nil { - panic(serverErr) - } - + th.App.StartServer() th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = prevListenAddress }) th.App.Srv.Store.MarkSystemRanUnitTests() th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.EnableOpenServer = true }) + utils.SetIsLicensed(enterprise) if enterprise { - th.App.SetLicense(model.NewTestLicense()) - } else { - th.App.SetLicense(nil) + utils.License().Features.SetDefaults() } return th diff --git a/app/channel.go b/app/channel.go index 8ac1f421c..e4bf48654 100644 --- a/app/channel.go +++ b/app/channel.go @@ -1359,7 +1359,7 @@ func (a *App) PermanentDeleteChannel(channel *model.Channel) *model.AppError { // This function is intended for use from the CLI. It is not robust against people joining the channel while the move // is in progress, and therefore should not be used from the API without first fixing this potential race condition. -func (a *App) MoveChannel(team *model.Team, channel *model.Channel, user *model.User) *model.AppError { +func (a *App) MoveChannel(team *model.Team, channel *model.Channel) *model.AppError { // Check that all channel members are in the destination team. if channelMembers, err := a.GetChannelMembersPage(channel.Id, 0, 10000000); err != nil { return err @@ -1378,37 +1378,11 @@ func (a *App) MoveChannel(team *model.Team, channel *model.Channel, user *model. } } - // keep instance of the previous team - var previousTeam *model.Team - if result := <-a.Srv.Store.Team().Get(channel.TeamId); result.Err != nil { - return result.Err - } else { - previousTeam = result.Data.(*model.Team) - } + // Change the Team ID of the channel. channel.TeamId = team.Id if result := <-a.Srv.Store.Channel().Update(channel); result.Err != nil { return result.Err } - a.postChannelMoveMessage(user, channel, previousTeam) - - return nil -} - -func (a *App) postChannelMoveMessage(user *model.User, channel *model.Channel, previousTeam *model.Team) *model.AppError { - - post := &model.Post{ - ChannelId: channel.Id, - Message: fmt.Sprintf(utils.T("api.team.move_channel.success"), previousTeam.Name), - Type: model.POST_MOVE_CHANNEL, - UserId: user.Id, - Props: model.StringInterface{ - "username": user.Username, - }, - } - - if _, err := a.CreatePost(post, channel, false); err != nil { - return model.NewAppError("postChannelMoveMessage", "api.team.move_channel.post.error", nil, err.Error(), http.StatusInternalServerError) - } return nil } diff --git a/app/channel_test.go b/app/channel_test.go index d315fbae6..a414fbb35 100644 --- a/app/channel_test.go +++ b/app/channel_test.go @@ -97,7 +97,7 @@ func TestMoveChannel(t *testing.T) { t.Fatal(err) } - if err := th.App.MoveChannel(targetTeam, channel1, th.BasicUser); err == nil { + if err := th.App.MoveChannel(targetTeam, channel1); err == nil { t.Fatal("Should have failed due to mismatched members.") } @@ -105,7 +105,7 @@ func TestMoveChannel(t *testing.T) { t.Fatal(err) } - if err := th.App.MoveChannel(targetTeam, channel1, th.BasicUser); err != nil { + if err := th.App.MoveChannel(targetTeam, channel1); err != nil { t.Fatal(err) } } diff --git a/app/config.go b/app/config.go index b4925e8fb..a2398f9e9 100644 --- a/app/config.go +++ b/app/config.go @@ -4,12 +4,7 @@ package app import ( - "crypto/ecdsa" - "crypto/elliptic" "crypto/md5" - "crypto/rand" - "crypto/x509" - "encoding/base64" "encoding/json" "fmt" "runtime/debug" @@ -121,91 +116,8 @@ func (a *App) InvokeConfigListeners(old, current *model.Config) { } } -// EnsureAsymmetricSigningKey ensures that an asymmetric signing key exists and future calls to -// AsymmetricSigningKey will always return a valid signing key. -func (a *App) ensureAsymmetricSigningKey() error { - if a.asymmetricSigningKey != nil { - return nil - } - - var key *model.SystemAsymmetricSigningKey - - result := <-a.Srv.Store.System().GetByName(model.SYSTEM_ASYMMETRIC_SIGNING_KEY) - if result.Err == nil { - if err := json.Unmarshal([]byte(result.Data.(*model.System).Value), &key); err != nil { - return err - } - } - - // If we don't already have a key, try to generate one. - if key == nil { - newECDSAKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - if err != nil { - return err - } - newKey := &model.SystemAsymmetricSigningKey{ - ECDSAKey: &model.SystemECDSAKey{ - Curve: "P-256", - X: newECDSAKey.X, - Y: newECDSAKey.Y, - D: newECDSAKey.D, - }, - } - system := &model.System{ - Name: model.SYSTEM_ASYMMETRIC_SIGNING_KEY, - } - v, err := json.Marshal(newKey) - if err != nil { - return err - } - system.Value = string(v) - if result = <-a.Srv.Store.System().Save(system); result.Err == nil { - // If we were able to save the key, use it, otherwise ignore the error. - key = newKey - } - } - - // If we weren't able to save a new key above, another server must have beat us to it. Get the - // key from the database, and if that fails, error out. - if key == nil { - result := <-a.Srv.Store.System().GetByName(model.SYSTEM_ASYMMETRIC_SIGNING_KEY) - if result.Err != nil { - return result.Err - } else if err := json.Unmarshal([]byte(result.Data.(*model.System).Value), &key); err != nil { - return err - } - } - - var curve elliptic.Curve - switch key.ECDSAKey.Curve { - case "P-256": - curve = elliptic.P256() - default: - return fmt.Errorf("unknown curve: " + key.ECDSAKey.Curve) - } - a.asymmetricSigningKey = &ecdsa.PrivateKey{ - PublicKey: ecdsa.PublicKey{ - Curve: curve, - X: key.ECDSAKey.X, - Y: key.ECDSAKey.Y, - }, - D: key.ECDSAKey.D, - } - a.regenerateClientConfig() - return nil -} - -// AsymmetricSigningKey will return a private key that can be used for asymmetric signing. -func (a *App) AsymmetricSigningKey() *ecdsa.PrivateKey { - return a.asymmetricSigningKey -} - func (a *App) regenerateClientConfig() { - a.clientConfig = utils.GenerateClientConfig(a.Config(), a.DiagnosticId(), a.License()) - if key := a.AsymmetricSigningKey(); key != nil { - der, _ := x509.MarshalPKIXPublicKey(&key.PublicKey) - a.clientConfig["AsymmetricSigningPublicKey"] = base64.StdEncoding.EncodeToString(der) - } + a.clientConfig = utils.GenerateClientConfig(a.Config(), a.DiagnosticId()) clientConfigJSON, _ := json.Marshal(a.clientConfig) a.clientConfigHash = fmt.Sprintf("%x", md5.Sum(clientConfigJSON)) } @@ -254,3 +166,11 @@ func (a *App) Desanitize(cfg *model.Config) { cfg.SqlSettings.DataSourceSearchReplicas[i] = actual.SqlSettings.DataSourceSearchReplicas[i] } } + +// License returns the currently active license or nil if the application is unlicensed. +func (a *App) License() *model.License { + if utils.IsLicensed() { + return utils.License() + } + return nil +} diff --git a/app/config_test.go b/app/config_test.go index 5ee999f0f..e3d50b958 100644 --- a/app/config_test.go +++ b/app/config_test.go @@ -6,8 +6,6 @@ package app import ( "testing" - "github.com/stretchr/testify/assert" - "github.com/mattermost/mattermost-server/model" ) @@ -56,10 +54,3 @@ func TestConfigListener(t *testing.T) { t.Fatal("listener 2 should've been called") } } - -func TestAsymmetricSigningKey(t *testing.T) { - th := Setup().InitBasic() - defer th.TearDown() - assert.NotNil(t, th.App.AsymmetricSigningKey()) - assert.NotEmpty(t, th.App.ClientConfig()["AsymmetricSigningPublicKey"]) -} diff --git a/app/diagnostics.go b/app/diagnostics.go index 6d83d3a89..809d9ff1e 100644 --- a/app/diagnostics.go +++ b/app/diagnostics.go @@ -501,7 +501,6 @@ func (a *App) trackConfig() { a.SendDiagnostic(TRACK_CONFIG_MESSAGE_EXPORT, map[string]interface{}{ "enable_message_export": *cfg.MessageExportSettings.EnableExport, - "export_format": *cfg.MessageExportSettings.ExportFormat, "daily_run_time": *cfg.MessageExportSettings.DailyRunTime, "default_export_from_timestamp": *cfg.MessageExportSettings.ExportFromTimestamp, "batch_size": *cfg.MessageExportSettings.BatchSize, diff --git a/app/email.go b/app/email.go index 54a272a3b..764dc017a 100644 --- a/app/email.go +++ b/app/email.go @@ -317,6 +317,5 @@ func (a *App) NewEmailTemplate(name, locale string) *utils.HTMLTemplate { } func (a *App) SendMail(to, subject, htmlBody string) *model.AppError { - license := a.License() - return utils.SendMailUsingConfig(to, subject, htmlBody, a.Config(), license != nil && *license.Features.Compliance) + return utils.SendMailUsingConfig(to, subject, htmlBody, a.Config()) } diff --git a/app/file.go b/app/file.go index bb20585bb..d66c64adb 100644 --- a/app/file.go +++ b/app/file.go @@ -58,8 +58,7 @@ const ( ) func (a *App) FileBackend() (utils.FileBackend, *model.AppError) { - license := a.License() - return utils.NewFileBackend(&a.Config().FileSettings, license != nil && *license.Features.Compliance) + return utils.NewFileBackend(&a.Config().FileSettings) } func (a *App) ReadFile(path string) ([]byte, *model.AppError) { diff --git a/app/license.go b/app/license.go index efb725a20..c7fd07197 100644 --- a/app/license.go +++ b/app/license.go @@ -4,19 +4,16 @@ package app import ( - "crypto/md5" - "fmt" "net/http" "strings" l4g "github.com/alecthomas/log4go" - "github.com/mattermost/mattermost-server/model" "github.com/mattermost/mattermost-server/utils" ) func (a *App) LoadLicense() { - a.SetLicense(nil) + utils.RemoveLicense() licenseId := "" if result := <-a.Srv.Store.System().Get(); result.Err == nil { @@ -39,7 +36,7 @@ func (a *App) LoadLicense() { if result := <-a.Srv.Store.License().Get(licenseId); result.Err == nil { record := result.Data.(*model.LicenseRecord) - a.ValidateAndSetLicenseBytes([]byte(record.Bytes)) + utils.LoadLicense([]byte(record.Bytes)) l4g.Info("License key valid unlocking enterprise features.") } else { l4g.Info(utils.T("mattermost.load_license.find.warn")) @@ -62,7 +59,7 @@ func (a *App) SaveLicense(licenseBytes []byte) (*model.License, *model.AppError) } } - if ok := a.SetLicense(license); !ok { + if ok := utils.SetLicense(license); !ok { return nil, model.NewAppError("addLicense", model.EXPIRED_LICENSE_ERROR, nil, "", http.StatusBadRequest) } @@ -105,115 +102,21 @@ func (a *App) SaveLicense(licenseBytes []byte) (*model.License, *model.AppError) return license, nil } -// License returns the currently active license or nil if the application is unlicensed. -func (a *App) License() *model.License { - license, _ := a.licenseValue.Load().(*model.License) - return license -} - -func (a *App) SetLicense(license *model.License) bool { - defer func() { - a.setDefaultRolesBasedOnConfig() - for _, listener := range a.licenseListeners { - listener() - } - }() - - if license != nil { - license.Features.SetDefaults() - - if !license.IsExpired() { - a.licenseValue.Store(license) - a.clientLicenseValue.Store(utils.GetClientLicense(license)) - return true - } - } - - a.licenseValue.Store((*model.License)(nil)) - a.SetClientLicense(map[string]string{"IsLicensed": "false"}) - return false -} - -func (a *App) ValidateAndSetLicenseBytes(b []byte) { - if success, licenseStr := utils.ValidateLicense(b); success { - license := model.LicenseFromJson(strings.NewReader(licenseStr)) - a.SetLicense(license) - return - } - - l4g.Warn(utils.T("utils.license.load_license.invalid.warn")) -} - -func (a *App) SetClientLicense(m map[string]string) { - a.clientLicenseValue.Store(m) -} - -func (a *App) ClientLicense() map[string]string { - clientLicense, _ := a.clientLicenseValue.Load().(map[string]string) - return clientLicense -} - func (a *App) RemoveLicense() *model.AppError { - if license, _ := a.licenseValue.Load().(*model.License); license == nil { - return nil - } + utils.RemoveLicense() sysVar := &model.System{} sysVar.Name = model.SYSTEM_ACTIVE_LICENSE_ID sysVar.Value = "" if result := <-a.Srv.Store.System().SaveOrUpdate(sysVar); result.Err != nil { + utils.RemoveLicense() return result.Err } - a.SetLicense(nil) a.ReloadConfig() a.InvalidateAllCaches() return nil } - -func (a *App) AddLicenseListener(listener func()) string { - id := model.NewId() - a.licenseListeners[id] = listener - return id -} - -func (a *App) RemoveLicenseListener(id string) { - delete(a.licenseListeners, id) -} - -func (a *App) GetClientLicenseEtag(useSanitized bool) string { - value := "" - - lic := a.ClientLicense() - - if useSanitized { - lic = a.GetSanitizedClientLicense() - } - - for k, v := range lic { - value += fmt.Sprintf("%s:%s;", k, v) - } - - return model.Etag(fmt.Sprintf("%x", md5.Sum([]byte(value)))) -} - -func (a *App) GetSanitizedClientLicense() map[string]string { - sanitizedLicense := make(map[string]string) - - for k, v := range a.ClientLicense() { - sanitizedLicense[k] = v - } - - delete(sanitizedLicense, "Id") - delete(sanitizedLicense, "Name") - delete(sanitizedLicense, "Email") - delete(sanitizedLicense, "PhoneNumber") - delete(sanitizedLicense, "IssuedAt") - delete(sanitizedLicense, "StartsAt") - delete(sanitizedLicense, "ExpiresAt") - - return sanitizedLicense -} diff --git a/app/license_test.go b/app/license_test.go index f86d604d1..5b73d9d18 100644 --- a/app/license_test.go +++ b/app/license_test.go @@ -4,9 +4,8 @@ package app import ( + //"github.com/mattermost/mattermost-server/model" "testing" - - "github.com/mattermost/mattermost-server/model" ) func TestLoadLicense(t *testing.T) { @@ -38,75 +37,3 @@ func TestRemoveLicense(t *testing.T) { t.Fatal("should have removed license") } } - -func TestSetLicense(t *testing.T) { - th := Setup() - defer th.TearDown() - - l1 := &model.License{} - l1.Features = &model.Features{} - l1.Customer = &model.Customer{} - l1.StartsAt = model.GetMillis() - 1000 - l1.ExpiresAt = model.GetMillis() + 100000 - if ok := th.App.SetLicense(l1); !ok { - t.Fatal("license should have worked") - } - - l2 := &model.License{} - l2.Features = &model.Features{} - l2.Customer = &model.Customer{} - l2.StartsAt = model.GetMillis() - 1000 - l2.ExpiresAt = model.GetMillis() - 100 - if ok := th.App.SetLicense(l2); ok { - t.Fatal("license should have failed") - } - - l3 := &model.License{} - l3.Features = &model.Features{} - l3.Customer = &model.Customer{} - l3.StartsAt = model.GetMillis() + 10000 - l3.ExpiresAt = model.GetMillis() + 100000 - if ok := th.App.SetLicense(l3); !ok { - t.Fatal("license should have passed") - } -} - -func TestClientLicenseEtag(t *testing.T) { - th := Setup() - defer th.TearDown() - - etag1 := th.App.GetClientLicenseEtag(false) - - th.App.SetClientLicense(map[string]string{"SomeFeature": "true", "IsLicensed": "true"}) - - etag2 := th.App.GetClientLicenseEtag(false) - if etag1 == etag2 { - t.Fatal("etags should not match") - } - - th.App.SetClientLicense(map[string]string{"SomeFeature": "true", "IsLicensed": "false"}) - - etag3 := th.App.GetClientLicenseEtag(false) - if etag2 == etag3 { - t.Fatal("etags should not match") - } -} - -func TestGetSanitizedClientLicense(t *testing.T) { - th := Setup() - defer th.TearDown() - - l1 := &model.License{} - l1.Features = &model.Features{} - l1.Customer = &model.Customer{} - l1.Customer.Name = "TestName" - l1.StartsAt = model.GetMillis() - 1000 - l1.ExpiresAt = model.GetMillis() + 100000 - th.App.SetLicense(l1) - - m := th.App.GetSanitizedClientLicense() - - if _, ok := m["Name"]; ok { - t.Fatal("should have been sanatized") - } -} diff --git a/app/plugin.go b/app/plugin.go index fe671d26a..3f06a000f 100644 --- a/app/plugin.go +++ b/app/plugin.go @@ -565,7 +565,6 @@ func (a *App) RegisterPluginCommand(pluginId string, command *model.Command) err TeamId: command.TeamId, AutoComplete: command.AutoComplete, AutoCompleteDesc: command.AutoCompleteDesc, - AutoCompleteHint: command.AutoCompleteHint, DisplayName: command.DisplayName, } diff --git a/app/role.go b/app/role.go index 9f271ea7a..5f39dd623 100644 --- a/app/role.go +++ b/app/role.go @@ -12,6 +12,8 @@ func (a *App) Role(id string) *model.Role { return a.roles[id] } -func (a *App) setDefaultRolesBasedOnConfig() { - a.roles = utils.DefaultRolesBasedOnConfig(a.Config(), a.License() != nil) +// Updates the roles based on the app config and the global license check. You may need to invoke +// this when license changes are made. +func (a *App) SetDefaultRolesBasedOnConfig() { + a.roles = utils.DefaultRolesBasedOnConfig(a.Config()) } diff --git a/app/server.go b/app/server.go index afa282ad6..1659908b6 100644 --- a/app/server.go +++ b/app/server.go @@ -17,7 +17,6 @@ import ( l4g "github.com/alecthomas/log4go" "github.com/gorilla/handlers" "github.com/gorilla/mux" - "github.com/pkg/errors" "golang.org/x/crypto/acme/autocert" "github.com/mattermost/mattermost-server/model" @@ -117,7 +116,7 @@ func redirectHTTPToHTTPS(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, url.String(), http.StatusFound) } -func (a *App) StartServer() error { +func (a *App) StartServer() { l4g.Info(utils.T("api.server.start_server.starting.info")) var handler http.Handler = &CorsWrapper{a.Config, a.Srv.Router} @@ -127,7 +126,8 @@ func (a *App) StartServer() error { rateLimiter, err := NewRateLimiter(&a.Config().RateLimitSettings) if err != nil { - return err + l4g.Critical(err.Error()) + return } a.Srv.RateLimiter = rateLimiter @@ -151,8 +151,8 @@ func (a *App) StartServer() error { listener, err := net.Listen("tcp", addr) if err != nil { - errors.Wrapf(err, utils.T("api.server.start_server.starting.critical"), err) - return err + l4g.Critical(utils.T("api.server.start_server.starting.critical"), err) + return } a.Srv.ListenAddr = listener.Addr().(*net.TCPAddr) @@ -214,8 +214,6 @@ func (a *App) StartServer() error { } close(a.Srv.didFinishListen) }() - - return nil } type tcpKeepAliveListener struct { diff --git a/app/server_test.go b/app/server_test.go deleted file mode 100644 index de358b976..000000000 --- a/app/server_test.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -package app - -import ( - "testing" - - "github.com/mattermost/mattermost-server/model" - "github.com/stretchr/testify/require" -) - -func TestStartServerSuccess(t *testing.T) { - a, err := New() - require.NoError(t, err) - - a.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = ":0" }) - serverErr := a.StartServer() - a.Shutdown() - require.NoError(t, serverErr) -} - -func TestStartServerRateLimiterCriticalError(t *testing.T) { - a, err := New() - require.NoError(t, err) - - // Attempt to use Rate Limiter with an invalid config - a.UpdateConfig(func(cfg *model.Config) { - *cfg.RateLimitSettings.Enable = true - *cfg.RateLimitSettings.MaxBurst = -100 - }) - - serverErr := a.StartServer() - a.Shutdown() - require.Error(t, serverErr) -} - -func TestStartServerPortUnavailable(t *testing.T) { - a, err := New() - require.NoError(t, err) - - // Attempt to listen on a system-reserved port - a.UpdateConfig(func(cfg *model.Config) { - *cfg.ServiceSettings.ListenAddress = ":21" - }) - - serverErr := a.StartServer() - a.Shutdown() - require.Error(t, serverErr) -} diff --git a/app/session_test.go b/app/session_test.go index bf8198a4e..bca3b59b7 100644 --- a/app/session_test.go +++ b/app/session_test.go @@ -6,10 +6,11 @@ package app import ( "testing" + "github.com/mattermost/mattermost-server/model" + "github.com/mattermost/mattermost-server/utils" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - - "github.com/mattermost/mattermost-server/model" ) func TestCache(t *testing.T) { @@ -47,7 +48,18 @@ func TestGetSessionIdleTimeoutInMinutes(t *testing.T) { session, _ = th.App.CreateSession(session) - th.App.SetLicense(model.NewTestLicense("compliance")) + isLicensed := utils.IsLicensed() + license := utils.License() + timeout := *th.App.Config().ServiceSettings.SessionIdleTimeoutInMinutes + defer func() { + utils.SetIsLicensed(isLicensed) + utils.SetLicense(license) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.SessionIdleTimeoutInMinutes = timeout }) + }() + utils.SetIsLicensed(true) + utils.SetLicense(&model.License{Features: &model.Features{}}) + utils.License().Features.SetDefaults() + *utils.License().Features.Compliance = true th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.SessionIdleTimeoutInMinutes = 5 }) rsession, err := th.App.GetSession(session.Token) @@ -110,7 +122,7 @@ func TestGetSessionIdleTimeoutInMinutes(t *testing.T) { assert.Nil(t, err) // Test regular session with license off, should not timeout - th.App.SetLicense(nil) + *utils.License().Features.Compliance = false session = &model.Session{ UserId: model.NewId(), @@ -124,7 +136,7 @@ func TestGetSessionIdleTimeoutInMinutes(t *testing.T) { _, err = th.App.GetSession(session.Token) assert.Nil(t, err) - th.App.SetLicense(model.NewTestLicense("compliance")) + *utils.License().Features.Compliance = true // Test regular session with timeout set to 0, should not timeout th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.SessionIdleTimeoutInMinutes = 0 }) diff --git a/cmd/platform/channel.go b/cmd/platform/channel.go index 5d86ad9da..98bdcebb8 100644 --- a/cmd/platform/channel.go +++ b/cmd/platform/channel.go @@ -106,8 +106,6 @@ func init() { channelCreateCmd.Flags().String("purpose", "", "Channel purpose") channelCreateCmd.Flags().Bool("private", false, "Create a private channel.") - moveChannelsCmd.Flags().String("username", "", "Required. Username who is moving the channel.") - deleteChannelsCmd.Flags().Bool("confirm", false, "Confirm you really want to delete the channels.") modifyChannelCmd.Flags().Bool("private", false, "Convert the channel to a private channel") @@ -321,33 +319,26 @@ func moveChannelsCmdF(cmd *cobra.Command, args []string) error { return errors.New("Unable to find destination team '" + args[0] + "'") } - username, erru := cmd.Flags().GetString("username") - if erru != nil || username == "" { - return errors.New("Username is required") - } - user := getUserFromUserArg(a, username) - channels := getChannelsFromChannelArgs(a, args[1:]) for i, channel := range channels { if channel == nil { CommandPrintErrorln("Unable to find channel '" + args[i] + "'") continue } - originTeamID := channel.TeamId - if err := moveChannel(a, team, channel, user); err != nil { + if err := moveChannel(a, team, channel); err != nil { CommandPrintErrorln("Unable to move channel '" + channel.Name + "' error: " + err.Error()) } else { - CommandPrettyPrintln("Moved channel '" + channel.Name + "' to " + team.Name + "(" + team.Id + ") from " + originTeamID + ".") + CommandPrettyPrintln("Moved channel '" + channel.Name + "'") } } return nil } -func moveChannel(a *app.App, team *model.Team, channel *model.Channel, user *model.User) *model.AppError { +func moveChannel(a *app.App, team *model.Team, channel *model.Channel) *model.AppError { oldTeamId := channel.TeamId - if err := a.MoveChannel(team, channel, user); err != nil { + if err := a.MoveChannel(team, channel); err != nil { return err } diff --git a/cmd/platform/channel_test.go b/cmd/platform/channel_test.go index cf8603cf3..1e6915679 100644 --- a/cmd/platform/channel_test.go +++ b/cmd/platform/channel_test.go @@ -44,33 +44,6 @@ func TestRemoveChannel(t *testing.T) { checkCommand(t, "channel", "remove", th.BasicTeam.Name+":"+channel.Name, th.BasicUser2.Email) } -func TestMoveChannel(t *testing.T) { - th := api.Setup().InitBasic() - defer th.TearDown() - - client := th.BasicClient - team1 := th.BasicTeam - team2 := th.CreateTeam(client) - user1 := th.BasicUser - th.LinkUserToTeam(user1, team2) - channel := th.BasicChannel - - th.LinkUserToTeam(user1, team1) - th.LinkUserToTeam(user1, team2) - - adminEmail := user1.Email - adminUsername := user1.Username - origin := team1.Name + ":" + channel.Name - dest := team2.Name - - checkCommand(t, "channel", "add", origin, adminEmail) - - // should fail with nill because errors are logged instead of returned when a channel does not exist - require.Nil(t, runCommand(t, "channel", "move", dest, team1.Name+":doesnotexist", "--username", adminUsername)) - - checkCommand(t, "channel", "move", dest, origin, "--username", adminUsername) -} - func TestListChannels(t *testing.T) { th := api.Setup().InitBasic() defer th.TearDown() diff --git a/cmd/platform/jobserver.go b/cmd/platform/jobserver.go index 044ee6b6a..e664136c0 100644 --- a/cmd/platform/jobserver.go +++ b/cmd/platform/jobserver.go @@ -35,7 +35,7 @@ func jobserverCmdF(cmd *cobra.Command, args []string) { defer l4g.Close() defer a.Shutdown() - a.LoadLicense() + a.Jobs.LoadLicense() // Run jobs l4g.Info("Starting Mattermost job server") diff --git a/cmd/platform/server.go b/cmd/platform/server.go index 1b411cf20..e3742cef6 100644 --- a/cmd/platform/server.go +++ b/cmd/platform/server.go @@ -42,11 +42,10 @@ func runServerCmd(cmd *cobra.Command, args []string) error { disableConfigWatch, _ := cmd.Flags().GetBool("disableconfigwatch") - interruptChan := make(chan os.Signal, 1) - return runServer(config, disableConfigWatch, interruptChan) + return runServer(config, disableConfigWatch) } -func runServer(configFileLocation string, disableConfigWatch bool, interruptChan chan os.Signal) error { +func runServer(configFileLocation string, disableConfigWatch bool) error { options := []app.Option{app.ConfigFile(configFileLocation)} if disableConfigWatch { options = append(options, app.DisableConfigWatch) @@ -54,7 +53,7 @@ func runServer(configFileLocation string, disableConfigWatch bool, interruptChan a, err := app.New(options...) if err != nil { - l4g.Critical(err.Error()) + l4g.Error(err.Error()) return err } defer a.Shutdown() @@ -88,12 +87,7 @@ func runServer(configFileLocation string, disableConfigWatch bool, interruptChan } }) - serverErr := a.StartServer() - if serverErr != nil { - l4g.Critical(serverErr.Error()) - return serverErr - } - + a.StartServer() api4.Init(a, a.Srv.Router, false) api3 := api.Init(a, a.Srv.Router) wsapi.Init(a, a.Srv.WebSocketRouter) @@ -166,8 +160,9 @@ func runServer(configFileLocation string, disableConfigWatch bool, interruptChan // wait for kill signal before attempting to gracefully shutdown // the running service - signal.Notify(interruptChan, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) - <-interruptChan + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) + <-c if a.Cluster != nil { a.Cluster.StopInterNodeCommunication() diff --git a/cmd/platform/server_test.go b/cmd/platform/server_test.go deleted file mode 100644 index 15f9a357a..000000000 --- a/cmd/platform/server_test.go +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -package main - -import ( - "io/ioutil" - "os" - "syscall" - "testing" - - "github.com/mattermost/mattermost-server/jobs" - "github.com/mattermost/mattermost-server/utils" - "github.com/stretchr/testify/require" -) - -type ServerTestHelper struct { - configPath string - disableConfigWatch bool - interruptChan chan os.Signal - originalInterval int -} - -func SetupServerTest() *ServerTestHelper { - // Build a channel that will be used by the server to receive system signals… - interruptChan := make(chan os.Signal, 1) - // …and sent it immediately a SIGINT value. - // This will make the server loop stop as soon as it started successfully. - interruptChan <- syscall.SIGINT - - // Let jobs poll for termination every 0.2s (instead of every 15s by default) - // Otherwise we would have to wait the whole polling duration before the test - // terminates. - originalInterval := jobs.DEFAULT_WATCHER_POLLING_INTERVAL - jobs.DEFAULT_WATCHER_POLLING_INTERVAL = 200 - - th := &ServerTestHelper{ - configPath: utils.FindConfigFile("config.json"), - disableConfigWatch: true, - interruptChan: interruptChan, - originalInterval: originalInterval, - } - return th -} - -func (th *ServerTestHelper) TearDownServerTest() { - jobs.DEFAULT_WATCHER_POLLING_INTERVAL = th.originalInterval -} - -func TestRunServerSuccess(t *testing.T) { - th := SetupServerTest() - defer th.TearDownServerTest() - - err := runServer(th.configPath, th.disableConfigWatch, th.interruptChan) - require.NoError(t, err) -} - -func TestRunServerInvalidConfigFile(t *testing.T) { - th := SetupServerTest() - defer th.TearDownServerTest() - - // Start the server with an unreadable config file - unreadableConfigFile, err := ioutil.TempFile("", "mattermost-unreadable-config-file-") - if err != nil { - panic(err) - } - os.Chmod(unreadableConfigFile.Name(), 0200) - defer os.Remove(unreadableConfigFile.Name()) - - err = runServer(unreadableConfigFile.Name(), th.disableConfigWatch, th.interruptChan) - require.Error(t, err) -} diff --git a/cmd/platform/test.go b/cmd/platform/test.go index 9ab3fbb36..036df07de 100644 --- a/cmd/platform/test.go +++ b/cmd/platform/test.go @@ -53,11 +53,7 @@ func webClientTestsCmdF(cmd *cobra.Command, args []string) error { defer a.Shutdown() utils.InitTranslations(a.Config().LocalizationSettings) - serverErr := a.StartServer() - if serverErr != nil { - return serverErr - } - + a.StartServer() api4.Init(a, a.Srv.Router, false) api.Init(a, a.Srv.Router) wsapi.Init(a, a.Srv.WebSocketRouter) @@ -75,11 +71,7 @@ func serverForWebClientTestsCmdF(cmd *cobra.Command, args []string) error { defer a.Shutdown() utils.InitTranslations(a.Config().LocalizationSettings) - serverErr := a.StartServer() - if serverErr != nil { - return serverErr - } - + a.StartServer() api4.Init(a, a.Srv.Router, false) api.Init(a, a.Srv.Router) wsapi.Init(a, a.Srv.WebSocketRouter) diff --git a/i18n/en.json b/i18n/en.json index de910fca8..4365a44fb 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -131,6 +131,10 @@ "id": "api.admin.upload_brand_image.too_large.app_error", "translation": "Unable to upload file. File is too large." }, + { + "id": "api.api.init.parsing_templates.debug", + "translation": "Parsing server templates at %v" + }, { "id": "api.api.init.parsing_templates.error", "translation": "Failed to parse server templates %v" @@ -3130,14 +3134,6 @@ "id": "app.channel.move_channel.members_do_not_match.error", "translation": "Cannot move a channel unless all its members are already members of the destination team." }, - { - "id": "api.team.move_channel.success", - "translation": "This channel has been moved to this team from %v." - }, - { - "id": "api.team.move_channel.post.error", - "translation": "Failed to post channel move message." - }, { "id": "app.channel.post_update_channel_purpose_message.post.error", "translation": "Failed to post channel purpose message" @@ -4862,14 +4858,6 @@ "id": "model.config.is_valid.message_export.batch_size.app_error", "translation": "Message export job BatchSize must be a positive integer" }, - { - "id": "model.config.is_valid.message_export.export_type.app_error", - "translation": "Message export job ExportFormat must be one of either 'actiance' or 'globalrelay'" - }, - { - "id": "model.config.is_valid.message_export.global_relay_email_address.app_error", - "translation": "Message export job GlobalRelayEmailAddress must be set to a valid email address" - }, { "id": "model.config.is_valid.message_export.daily_runtime.app_error", "translation": "Message export job DailyRuntime must be a 24-hour time stamp in the form HH:MM." @@ -7094,10 +7082,6 @@ "id": "utils.mail.new_client.auth.app_error", "translation": "Failed to authenticate on SMTP server" }, - { - "id": "utils.mail.sendMail.attachments.write_error", - "translation": "Failed to write attachment to email" - }, { "id": "utils.mail.new_client.helo.error", "translation": "Failed to to set the HELO to SMTP server %v" @@ -7174,6 +7158,10 @@ "id": "web.create_dir.error", "translation": "Failed to create directory watcher %v" }, + { + "id": "web.dir_fail.error", + "translation": "Failed in directory watcher %v" + }, { "id": "web.do_load_channel.error", "translation": "Error in getting users profile for id=%v forcing logout" @@ -7278,10 +7266,18 @@ "id": "web.parsing_templates.debug", "translation": "Parsing templates at %v" }, + { + "id": "web.parsing_templates.error", + "translation": "Failed to parse templates %v" + }, { "id": "web.post_permalink.app_error", "translation": "Invalid Post ID" }, + { + "id": "web.reparse_templates.info", + "translation": "Re-parsing templates because of modified file %v" + }, { "id": "web.reset_password.expired_link.app_error", "translation": "The password reset link has expired" diff --git a/jobs/jobs_watcher.go b/jobs/jobs_watcher.go index eaa3a4e73..f519e7cca 100644 --- a/jobs/jobs_watcher.go +++ b/jobs/jobs_watcher.go @@ -11,9 +11,9 @@ import ( "github.com/mattermost/mattermost-server/model" ) -// Default polling interval for jobs termination. -// (Defining as `var` rather than `const` allows tests to lower the interval.) -var DEFAULT_WATCHER_POLLING_INTERVAL = 15000 +const ( + DEFAULT_WATCHER_POLLING_INTERVAL = 15000 +) type Watcher struct { srv *JobServer diff --git a/jobs/server.go b/jobs/server.go index 01cf821dc..4015d581e 100644 --- a/jobs/server.go +++ b/jobs/server.go @@ -4,9 +4,12 @@ package jobs import ( + l4g "github.com/alecthomas/log4go" + ejobs "github.com/mattermost/mattermost-server/einterfaces/jobs" "github.com/mattermost/mattermost-server/model" "github.com/mattermost/mattermost-server/store" + "github.com/mattermost/mattermost-server/utils" ) type ConfigService interface { @@ -47,6 +50,36 @@ func (srv *JobServer) Config() *model.Config { return srv.ConfigService.Config() } +func (srv *JobServer) LoadLicense() { + licenseId := "" + if result := <-srv.Store.System().Get(); result.Err == nil { + props := result.Data.(model.StringMap) + licenseId = props[model.SYSTEM_ACTIVE_LICENSE_ID] + } + + var licenseBytes []byte + + if len(licenseId) != 26 { + // Lets attempt to load the file from disk since it was missing from the DB + _, licenseBytes = utils.GetAndValidateLicenseFileFromDisk(*srv.ConfigService.Config().ServiceSettings.LicenseFileLocation) + } else { + if result := <-srv.Store.License().Get(licenseId); result.Err == nil { + record := result.Data.(*model.LicenseRecord) + licenseBytes = []byte(record.Bytes) + l4g.Info("License key valid unlocking enterprise features.") + } else { + l4g.Info(utils.T("mattermost.load_license.find.warn")) + } + } + + if licenseBytes != nil { + utils.LoadLicense(licenseBytes) + l4g.Info("License key valid unlocking enterprise features.") + } else { + l4g.Info(utils.T("mattermost.load_license.find.warn")) + } +} + func (srv *JobServer) StartWorkers() { srv.Workers = srv.InitWorkers().Start() } diff --git a/jobs/server_test.go b/jobs/server_test.go new file mode 100644 index 000000000..3b5ef6f3d --- /dev/null +++ b/jobs/server_test.go @@ -0,0 +1,39 @@ +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package jobs + +import ( + "testing" + + "github.com/mattermost/mattermost-server/model" + "github.com/mattermost/mattermost-server/store" + "github.com/mattermost/mattermost-server/store/storetest" + "github.com/mattermost/mattermost-server/utils" +) + +func TestJobServer_LoadLicense(t *testing.T) { + if utils.T == nil { + utils.TranslationsPreInit() + } + + mockStore := &storetest.Store{} + defer mockStore.AssertExpectations(t) + + server := &JobServer{ + Store: mockStore, + } + + mockStore.SystemStore.On("Get").Return(storetest.NewStoreChannel(store.StoreResult{ + Data: model.StringMap{ + model.SYSTEM_ACTIVE_LICENSE_ID: "thelicenseid00000000000000", + }, + })) + mockStore.LicenseStore.On("Get", "thelicenseid00000000000000").Return(storetest.NewStoreChannel(store.StoreResult{ + Data: &model.LicenseRecord{ + Id: "thelicenseid00000000000000", + }, + })) + + server.LoadLicense() +} diff --git a/model/channel_member_history.go b/model/channel_member_history.go index 47c59d54e..bc71b580a 100644 --- a/model/channel_member_history.go +++ b/model/channel_member_history.go @@ -6,10 +6,7 @@ package model type ChannelMemberHistory struct { ChannelId string UserId string + UserEmail string `db:"Email"` JoinTime int64 LeaveTime *int64 - - // these two fields are never set in the database - when we SELECT, we join on Users to get them - UserEmail string `db:"Email"` - Username string } diff --git a/model/client4.go b/model/client4.go index 962b816bb..0694ecbdf 100644 --- a/model/client4.go +++ b/model/client4.go @@ -1729,7 +1729,7 @@ func (c *Client4) RemoveUserFromChannel(channelId, userId string) (bool, *Respon // CreatePost creates a post based on the provided post struct. func (c *Client4) CreatePost(post *Post) (*Post, *Response) { - if r, err := c.DoApiPost(c.GetPostsRoute(), post.ToUnsanitizedJson()); err != nil { + if r, err := c.DoApiPost(c.GetPostsRoute(), post.ToJson()); err != nil { return nil, BuildErrorResponse(r, err) } else { defer closeBody(r) @@ -1739,7 +1739,7 @@ func (c *Client4) CreatePost(post *Post) (*Post, *Response) { // UpdatePost updates a post based on the provided post struct. func (c *Client4) UpdatePost(postId string, post *Post) (*Post, *Response) { - if r, err := c.DoApiPut(c.GetPostRoute(postId), post.ToUnsanitizedJson()); err != nil { + if r, err := c.DoApiPut(c.GetPostRoute(postId), post.ToJson()); err != nil { return nil, BuildErrorResponse(r, err) } else { defer closeBody(r) diff --git a/model/client4_test.go b/model/client4_test.go deleted file mode 100644 index f7923fa8f..000000000 --- a/model/client4_test.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -package model - -import ( - "net/http" - "net/http/httptest" - "testing" - - "github.com/stretchr/testify/assert" -) - -// https://github.com/mattermost/mattermost-server/issues/8205 -func TestClient4CreatePost(t *testing.T) { - post := &Post{ - Props: map[string]interface{}{ - "attachments": []*SlackAttachment{ - &SlackAttachment{ - Actions: []*PostAction{ - &PostAction{ - Integration: &PostActionIntegration{ - Context: map[string]interface{}{ - "foo": "bar", - }, - URL: "http://foo.com", - }, - Name: "Foo", - }, - }, - }, - }, - }, - } - - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - attachments := PostFromJson(r.Body).Attachments() - assert.Equal(t, []*SlackAttachment{ - &SlackAttachment{ - Actions: []*PostAction{ - &PostAction{ - Integration: &PostActionIntegration{ - Context: map[string]interface{}{ - "foo": "bar", - }, - URL: "http://foo.com", - }, - Name: "Foo", - }, - }, - }, - }, attachments) - })) - - client := NewAPIv4Client(server.URL) - _, resp := client.CreatePost(post) - assert.Equal(t, http.StatusOK, resp.StatusCode) -} diff --git a/model/config.go b/model/config.go index 9010eaeae..20011f7cb 100644 --- a/model/config.go +++ b/model/config.go @@ -158,9 +158,6 @@ const ( PLUGIN_SETTINGS_DEFAULT_DIRECTORY = "./plugins" PLUGIN_SETTINGS_DEFAULT_CLIENT_DIRECTORY = "./client/plugins" - - COMPLIANCE_EXPORT_TYPE_ACTIANCE = "actiance" - COMPLIANCE_EXPORT_TYPE_GLOBALRELAY = "globalrelay" ) type ServiceSettings struct { @@ -1626,13 +1623,9 @@ func (s *PluginSettings) SetDefaults() { type MessageExportSettings struct { EnableExport *bool - ExportFormat *string DailyRunTime *string ExportFromTimestamp *int64 BatchSize *int - - // formatter-specific settings - these are only expected to be non-nil if ExportFormat is set to the associated format - GlobalRelayEmailAddress *string } func (s *MessageExportSettings) SetDefaults() { @@ -1640,10 +1633,6 @@ func (s *MessageExportSettings) SetDefaults() { s.EnableExport = NewBool(false) } - if s.ExportFormat == nil { - s.ExportFormat = NewString(COMPLIANCE_EXPORT_TYPE_ACTIANCE) - } - if s.DailyRunTime == nil { s.DailyRunTime = NewString("01:00") } @@ -2181,16 +2170,6 @@ func (mes *MessageExportSettings) isValid(fs FileSettings) *AppError { return NewAppError("Config.IsValid", "model.config.is_valid.message_export.daily_runtime.app_error", nil, err.Error(), http.StatusBadRequest) } else if mes.BatchSize == nil || *mes.BatchSize < 0 { return NewAppError("Config.IsValid", "model.config.is_valid.message_export.batch_size.app_error", nil, "", http.StatusBadRequest) - } else if mes.ExportFormat == nil || (*mes.ExportFormat != COMPLIANCE_EXPORT_TYPE_ACTIANCE && *mes.ExportFormat != COMPLIANCE_EXPORT_TYPE_GLOBALRELAY) { - return NewAppError("Config.IsValid", "model.config.is_valid.message_export.export_type.app_error", nil, "", http.StatusBadRequest) - } - - if *mes.ExportFormat == COMPLIANCE_EXPORT_TYPE_GLOBALRELAY { - // validating email addresses is hard - just make sure it contains an '@' sign - // see https://stackoverflow.com/questions/201323/using-a-regular-expression-to-validate-an-email-address - if mes.GlobalRelayEmailAddress == nil || !strings.Contains(*mes.GlobalRelayEmailAddress, "@") { - return NewAppError("Config.IsValid", "model.config.is_valid.message_export.global_relay_email_address.app_error", nil, "", http.StatusBadRequest) - } } } return nil diff --git a/model/config_test.go b/model/config_test.go index 919f73fd7..5510c40d0 100644 --- a/model/config_test.go +++ b/model/config_test.go @@ -136,7 +136,7 @@ func TestMessageExportSettingsIsValidBatchSizeInvalid(t *testing.T) { require.Error(t, mes.isValid(*fs)) } -func TestMessageExportSettingsIsValidExportFormatInvalid(t *testing.T) { +func TestMessageExportSettingsIsValid(t *testing.T) { fs := &FileSettings{ DriverName: NewString("foo"), // bypass file location check } @@ -147,55 +147,6 @@ func TestMessageExportSettingsIsValidExportFormatInvalid(t *testing.T) { BatchSize: NewInt(100), } - // should fail fast because export format isn't set - require.Error(t, mes.isValid(*fs)) -} - -func TestMessageExportSettingsIsValidGlobalRelayEmailAddressInvalid(t *testing.T) { - fs := &FileSettings{ - DriverName: NewString("foo"), // bypass file location check - } - mes := &MessageExportSettings{ - EnableExport: NewBool(true), - ExportFormat: NewString(COMPLIANCE_EXPORT_TYPE_GLOBALRELAY), - ExportFromTimestamp: NewInt64(0), - DailyRunTime: NewString("15:04"), - BatchSize: NewInt(100), - } - - // should fail fast because global relay email address isn't set - require.Error(t, mes.isValid(*fs)) -} - -func TestMessageExportSettingsIsValidActiance(t *testing.T) { - fs := &FileSettings{ - DriverName: NewString("foo"), // bypass file location check - } - mes := &MessageExportSettings{ - EnableExport: NewBool(true), - ExportFormat: NewString(COMPLIANCE_EXPORT_TYPE_ACTIANCE), - ExportFromTimestamp: NewInt64(0), - DailyRunTime: NewString("15:04"), - BatchSize: NewInt(100), - } - - // should pass because everything is valid - require.Nil(t, mes.isValid(*fs)) -} - -func TestMessageExportSettingsIsValidGlobalRelay(t *testing.T) { - fs := &FileSettings{ - DriverName: NewString("foo"), // bypass file location check - } - mes := &MessageExportSettings{ - EnableExport: NewBool(true), - ExportFormat: NewString(COMPLIANCE_EXPORT_TYPE_GLOBALRELAY), - ExportFromTimestamp: NewInt64(0), - DailyRunTime: NewString("15:04"), - BatchSize: NewInt(100), - GlobalRelayEmailAddress: NewString("test@mattermost.com"), - } - // should pass because everything is valid require.Nil(t, mes.isValid(*fs)) } @@ -208,7 +159,6 @@ func TestMessageExportSetDefaults(t *testing.T) { require.Equal(t, "01:00", *mes.DailyRunTime) require.Equal(t, int64(0), *mes.ExportFromTimestamp) require.Equal(t, 10000, *mes.BatchSize) - require.Equal(t, COMPLIANCE_EXPORT_TYPE_ACTIANCE, *mes.ExportFormat) } func TestMessageExportSetDefaultsExportEnabledExportFromTimestampNil(t *testing.T) { diff --git a/model/license.go b/model/license.go index 942a18d55..f96cba06c 100644 --- a/model/license.go +++ b/model/license.go @@ -173,25 +173,6 @@ func (l *License) ToJson() string { return string(b) } -// NewTestLicense returns a license that expires in the future and has the given features. -func NewTestLicense(features ...string) *License { - ret := &License{ - ExpiresAt: GetMillis() + 90*24*60*60*1000, - Customer: &Customer{}, - Features: &Features{}, - } - ret.Features.SetDefaults() - - featureMap := map[string]bool{} - for _, feature := range features { - featureMap[feature] = true - } - featureJson, _ := json.Marshal(featureMap) - json.Unmarshal(featureJson, &ret.Features) - - return ret -} - func LicenseFromJson(data io.Reader) *License { var o *License json.NewDecoder(data).Decode(&o) diff --git a/model/message_export.go b/model/message_export.go index 22641deee..b59b114d4 100644 --- a/model/message_export.go +++ b/model/message_export.go @@ -9,7 +9,6 @@ type MessageExport struct { UserId *string UserEmail *string - Username *string PostId *string PostCreateAt *int64 diff --git a/model/post.go b/model/post.go index 4a774b5d4..391b948f4 100644 --- a/model/post.go +++ b/model/post.go @@ -28,7 +28,6 @@ const ( POST_ADD_REMOVE = "system_add_remove" // Deprecated, use POST_ADD_TO_CHANNEL or POST_REMOVE_FROM_CHANNEL instead POST_ADD_TO_CHANNEL = "system_add_to_channel" POST_REMOVE_FROM_CHANNEL = "system_remove_from_channel" - POST_MOVE_CHANNEL = "system_move_channel" POST_ADD_TO_TEAM = "system_add_to_team" POST_REMOVE_FROM_TEAM = "system_remove_from_team" POST_HEADER_CHANGE = "system_header_change" @@ -122,13 +121,12 @@ type PostActionIntegrationResponse struct { func (o *Post) ToJson() string { copy := *o copy.StripActionIntegrations() - b, _ := json.Marshal(©) - return string(b) -} - -func (o *Post) ToUnsanitizedJson() string { - b, _ := json.Marshal(o) - return string(b) + b, err := json.Marshal(©) + if err != nil { + return "" + } else { + return string(b) + } } func PostFromJson(data io.Reader) *Post { @@ -198,7 +196,6 @@ func (o *Post) IsValid() *AppError { POST_LEAVE_TEAM, POST_ADD_TO_CHANNEL, POST_REMOVE_FROM_CHANNEL, - POST_MOVE_CHANNEL, POST_ADD_TO_TEAM, POST_REMOVE_FROM_TEAM, POST_SLACK_ATTACHMENT, diff --git a/model/system.go b/model/system.go index 2a636b14f..020c50858 100644 --- a/model/system.go +++ b/model/system.go @@ -6,16 +6,14 @@ package model import ( "encoding/json" "io" - "math/big" ) const ( - SYSTEM_DIAGNOSTIC_ID = "DiagnosticId" - SYSTEM_RAN_UNIT_TESTS = "RanUnitTests" - SYSTEM_LAST_SECURITY_TIME = "LastSecurityTime" - SYSTEM_ACTIVE_LICENSE_ID = "ActiveLicenseId" - SYSTEM_LAST_COMPLIANCE_TIME = "LastComplianceTime" - SYSTEM_ASYMMETRIC_SIGNING_KEY = "AsymmetricSigningKey" + SYSTEM_DIAGNOSTIC_ID = "DiagnosticId" + SYSTEM_RAN_UNIT_TESTS = "RanUnitTests" + SYSTEM_LAST_SECURITY_TIME = "LastSecurityTime" + SYSTEM_ACTIVE_LICENSE_ID = "ActiveLicenseId" + SYSTEM_LAST_COMPLIANCE_TIME = "LastComplianceTime" ) type System struct { @@ -33,14 +31,3 @@ func SystemFromJson(data io.Reader) *System { json.NewDecoder(data).Decode(&o) return o } - -type SystemAsymmetricSigningKey struct { - ECDSAKey *SystemECDSAKey `json:"ecdsa_key,omitempty"` -} - -type SystemECDSAKey struct { - Curve string `json:"curve"` - X *big.Int `json:"x"` - Y *big.Int `json:"y"` - D *big.Int `json:"d,omitempty"` -} diff --git a/store/sqlstore/channel_member_history_store.go b/store/sqlstore/channel_member_history_store.go index 0b86aac28..182f37ce9 100644 --- a/store/sqlstore/channel_member_history_store.go +++ b/store/sqlstore/channel_member_history_store.go @@ -110,8 +110,7 @@ func (s SqlChannelMemberHistoryStore) getFromChannelMemberHistoryTable(startTime query := ` SELECT cmh.*, - u.Email, - u.Username + u.Email FROM ChannelMemberHistory cmh INNER JOIN Users u ON cmh.UserId = u.Id WHERE cmh.ChannelId = :ChannelId @@ -131,10 +130,9 @@ func (s SqlChannelMemberHistoryStore) getFromChannelMemberHistoryTable(startTime func (s SqlChannelMemberHistoryStore) getFromChannelMembersTable(startTime int64, endTime int64, channelId string) ([]*model.ChannelMemberHistory, error) { query := ` SELECT DISTINCT - ch.ChannelId, - ch.UserId, - u.Email, - u.Username + ch.ChannelId, + ch.UserId, + u.email FROM ChannelMembers AS ch INNER JOIN Users AS u ON ch.UserId = u.id WHERE ch.ChannelId = :ChannelId` @@ -160,7 +158,7 @@ func (s SqlChannelMemberHistoryStore) PermanentDeleteBatch(endTime int64, limit query = `DELETE FROM ChannelMemberHistory WHERE ctid IN ( - SELECT ctid FROM ChannelMemberHistory + SELECT ctid FROM ChannelMemberHistory WHERE LeaveTime IS NOT NULL AND LeaveTime <= :EndTime LIMIT :Limit diff --git a/store/sqlstore/compliance_store.go b/store/sqlstore/compliance_store.go index 03d92d5e1..a25b01548 100644 --- a/store/sqlstore/compliance_store.go +++ b/store/sqlstore/compliance_store.go @@ -225,8 +225,7 @@ func (s SqlComplianceStore) MessageExport(after int64, limit int) store.StoreCha Channels.Id AS ChannelId, Channels.DisplayName AS ChannelDisplayName, Users.Id AS UserId, - Users.Email AS UserEmail, - Users.Username + Users.Email AS UserEmail FROM Posts LEFT OUTER JOIN Channels ON Posts.ChannelId = Channels.Id diff --git a/store/sqlstore/post_store.go b/store/sqlstore/post_store.go index 25c3c4913..bc336e70d 100644 --- a/store/sqlstore/post_store.go +++ b/store/sqlstore/post_store.go @@ -322,10 +322,7 @@ type etagPosts struct { func (s SqlPostStore) InvalidateLastPostTimeCache(channelId string) { lastPostTimeCache.Remove(channelId) - - // Keys are "{channelid}{limit}" and caching only occurs on limits of 30 and 60 - lastPostsCache.Remove(channelId + "30") - lastPostsCache.Remove(channelId + "60") + lastPostsCache.Remove(channelId) } func (s SqlPostStore) GetEtag(channelId string, allowFromCache bool) store.StoreChannel { @@ -442,9 +439,8 @@ func (s SqlPostStore) GetPosts(channelId string, offset int, limit int, allowFro return } - // Caching only occurs on limits of 30 and 60, the common limits requested by MM clients - if allowFromCache && offset == 0 && (limit == 60 || limit == 30) { - if cacheItem, ok := lastPostsCache.Get(fmt.Sprintf("%s%v", channelId, limit)); ok { + if allowFromCache && offset == 0 && limit == 60 { + if cacheItem, ok := lastPostsCache.Get(channelId); ok { if s.metrics != nil { s.metrics.IncrementMemCacheHitCounter("Last Posts Cache") } @@ -486,9 +482,8 @@ func (s SqlPostStore) GetPosts(channelId string, offset int, limit int, allowFro list.MakeNonNil() - // Caching only occurs on limits of 30 and 60, the common limits requested by MM clients - if offset == 0 && (limit == 60 || limit == 30) { - lastPostsCache.AddWithExpiresInSecs(fmt.Sprintf("%s%v", channelId, limit), list, LAST_POSTS_CACHE_SEC) + if offset == 0 && limit == 60 { + lastPostsCache.AddWithExpiresInSecs(channelId, list, LAST_POSTS_CACHE_SEC) } result.Data = list diff --git a/store/sqlstore/upgrade.go b/store/sqlstore/upgrade.go index 56fdf9d6c..7c1522f25 100644 --- a/store/sqlstore/upgrade.go +++ b/store/sqlstore/upgrade.go @@ -15,7 +15,6 @@ import ( ) const ( - VERSION_4_8_0 = "4.8.0" VERSION_4_7_0 = "4.7.0" VERSION_4_6_0 = "4.6.0" VERSION_4_5_0 = "4.5.0" @@ -65,7 +64,6 @@ func UpgradeDatabase(sqlStore SqlStore) { UpgradeDatabaseToVersion45(sqlStore) UpgradeDatabaseToVersion46(sqlStore) UpgradeDatabaseToVersion47(sqlStore) - UpgradeDatabaseToVersion48(sqlStore) // If the SchemaVersion is empty this this is the first time it has ran // so lets set it to the current version. @@ -349,10 +347,3 @@ func UpgradeDatabaseToVersion47(sqlStore SqlStore) { saveSchemaVersion(sqlStore, VERSION_4_7_0) } } - -func UpgradeDatabaseToVersion48(sqlStore SqlStore) { - //TODO: Uncomment the following condition when version 4.8.0 is released - //if shouldPerformUpgrade(sqlStore, VERSION_4_7_0, VERSION_4_8_0) { - // saveSchemaVersion(sqlStore, VERSION_4_8_0) - //} -} diff --git a/store/storetest/channel_member_history_store.go b/store/storetest/channel_member_history_store.go index fa2e7a8fa..6fe73478c 100644 --- a/store/storetest/channel_member_history_store.go +++ b/store/storetest/channel_member_history_store.go @@ -35,7 +35,6 @@ func testLogJoinEvent(t *testing.T, ss store.Store) { user := model.User{ Email: model.NewId() + "@mattermost.com", Nickname: model.NewId(), - Username: model.NewId(), } user = *store.Must(ss.User().Save(&user)).(*model.User) @@ -58,7 +57,6 @@ func testLogLeaveEvent(t *testing.T, ss store.Store) { user := model.User{ Email: model.NewId() + "@mattermost.com", Nickname: model.NewId(), - Username: model.NewId(), } user = *store.Must(ss.User().Save(&user)).(*model.User) @@ -84,7 +82,6 @@ func testGetUsersInChannelAtChannelMemberHistory(t *testing.T, ss store.Store) { user := model.User{ Email: model.NewId() + "@mattermost.com", Nickname: model.NewId(), - Username: model.NewId(), } user = *store.Must(ss.User().Save(&user)).(*model.User) @@ -111,7 +108,6 @@ func testGetUsersInChannelAtChannelMemberHistory(t *testing.T, ss store.Store) { assert.Equal(t, channel.Id, channelMembers[0].ChannelId) assert.Equal(t, user.Id, channelMembers[0].UserId) assert.Equal(t, user.Email, channelMembers[0].UserEmail) - assert.Equal(t, user.Username, channelMembers[0].Username) assert.Equal(t, joinTime, channelMembers[0].JoinTime) assert.Nil(t, channelMembers[0].LeaveTime) @@ -121,7 +117,6 @@ func testGetUsersInChannelAtChannelMemberHistory(t *testing.T, ss store.Store) { assert.Equal(t, channel.Id, channelMembers[0].ChannelId) assert.Equal(t, user.Id, channelMembers[0].UserId) assert.Equal(t, user.Email, channelMembers[0].UserEmail) - assert.Equal(t, user.Username, channelMembers[0].Username) assert.Equal(t, joinTime, channelMembers[0].JoinTime) assert.Nil(t, channelMembers[0].LeaveTime) @@ -134,7 +129,6 @@ func testGetUsersInChannelAtChannelMemberHistory(t *testing.T, ss store.Store) { assert.Equal(t, channel.Id, channelMembers[0].ChannelId) assert.Equal(t, user.Id, channelMembers[0].UserId) assert.Equal(t, user.Email, channelMembers[0].UserEmail) - assert.Equal(t, user.Username, channelMembers[0].Username) assert.Equal(t, joinTime, channelMembers[0].JoinTime) assert.Equal(t, leaveTime, *channelMembers[0].LeaveTime) @@ -144,7 +138,6 @@ func testGetUsersInChannelAtChannelMemberHistory(t *testing.T, ss store.Store) { assert.Equal(t, channel.Id, channelMembers[0].ChannelId) assert.Equal(t, user.Id, channelMembers[0].UserId) assert.Equal(t, user.Email, channelMembers[0].UserEmail) - assert.Equal(t, user.Username, channelMembers[0].Username) assert.Equal(t, joinTime, channelMembers[0].JoinTime) assert.Equal(t, leaveTime, *channelMembers[0].LeaveTime) @@ -167,7 +160,6 @@ func testGetUsersInChannelAtChannelMembers(t *testing.T, ss store.Store) { user := model.User{ Email: model.NewId() + "@mattermost.com", Nickname: model.NewId(), - Username: model.NewId(), } user = *store.Must(ss.User().Save(&user)).(*model.User) @@ -200,7 +192,6 @@ func testGetUsersInChannelAtChannelMembers(t *testing.T, ss store.Store) { assert.Equal(t, channel.Id, channelMembers[0].ChannelId) assert.Equal(t, user.Id, channelMembers[0].UserId) assert.Equal(t, user.Email, channelMembers[0].UserEmail) - assert.Equal(t, user.Username, channelMembers[0].Username) assert.Equal(t, joinTime-500, channelMembers[0].JoinTime) assert.Equal(t, joinTime-100, *channelMembers[0].LeaveTime) @@ -210,7 +201,6 @@ func testGetUsersInChannelAtChannelMembers(t *testing.T, ss store.Store) { assert.Equal(t, channel.Id, channelMembers[0].ChannelId) assert.Equal(t, user.Id, channelMembers[0].UserId) assert.Equal(t, user.Email, channelMembers[0].UserEmail) - assert.Equal(t, user.Username, channelMembers[0].Username) assert.Equal(t, joinTime-100, channelMembers[0].JoinTime) assert.Equal(t, joinTime+500, *channelMembers[0].LeaveTime) @@ -220,7 +210,6 @@ func testGetUsersInChannelAtChannelMembers(t *testing.T, ss store.Store) { assert.Equal(t, channel.Id, channelMembers[0].ChannelId) assert.Equal(t, user.Id, channelMembers[0].UserId) assert.Equal(t, user.Email, channelMembers[0].UserEmail) - assert.Equal(t, user.Username, channelMembers[0].Username) assert.Equal(t, joinTime+100, channelMembers[0].JoinTime) assert.Equal(t, joinTime+500, *channelMembers[0].LeaveTime) @@ -230,7 +219,6 @@ func testGetUsersInChannelAtChannelMembers(t *testing.T, ss store.Store) { assert.Equal(t, channel.Id, channelMembers[0].ChannelId) assert.Equal(t, user.Id, channelMembers[0].UserId) assert.Equal(t, user.Email, channelMembers[0].UserEmail) - assert.Equal(t, user.Username, channelMembers[0].Username) assert.Equal(t, joinTime+100, channelMembers[0].JoinTime) assert.Equal(t, leaveTime-100, *channelMembers[0].LeaveTime) @@ -240,7 +228,6 @@ func testGetUsersInChannelAtChannelMembers(t *testing.T, ss store.Store) { assert.Equal(t, channel.Id, channelMembers[0].ChannelId) assert.Equal(t, user.Id, channelMembers[0].UserId) assert.Equal(t, user.Email, channelMembers[0].UserEmail) - assert.Equal(t, user.Username, channelMembers[0].Username) assert.Equal(t, joinTime-100, channelMembers[0].JoinTime) assert.Equal(t, leaveTime+100, *channelMembers[0].LeaveTime) @@ -250,7 +237,6 @@ func testGetUsersInChannelAtChannelMembers(t *testing.T, ss store.Store) { assert.Equal(t, channel.Id, channelMembers[0].ChannelId) assert.Equal(t, user.Id, channelMembers[0].UserId) assert.Equal(t, user.Email, channelMembers[0].UserEmail) - assert.Equal(t, user.Username, channelMembers[0].Username) assert.Equal(t, leaveTime+100, channelMembers[0].JoinTime) assert.Equal(t, leaveTime+200, *channelMembers[0].LeaveTime) } @@ -269,14 +255,12 @@ func testPermanentDeleteBatch(t *testing.T, ss store.Store) { user := model.User{ Email: model.NewId() + "@mattermost.com", Nickname: model.NewId(), - Username: model.NewId(), } user = *store.Must(ss.User().Save(&user)).(*model.User) user2 := model.User{ Email: model.NewId() + "@mattermost.com", Nickname: model.NewId(), - Username: model.NewId(), } user2 = *store.Must(ss.User().Save(&user2)).(*model.User) diff --git a/store/storetest/compliance_store.go b/store/storetest/compliance_store.go index eb29bedc7..c5bd60f05 100644 --- a/store/storetest/compliance_store.go +++ b/store/storetest/compliance_store.go @@ -341,8 +341,7 @@ func testComplianceMessageExport(t *testing.T, ss store.Store) { // and two users that are a part of that team user1 := &model.User{ - Email: model.NewId(), - Username: model.NewId(), + Email: model.NewId(), } user1 = store.Must(ss.User().Save(user1)).(*model.User) store.Must(ss.Team().SaveMember(&model.TeamMember{ @@ -351,8 +350,7 @@ func testComplianceMessageExport(t *testing.T, ss store.Store) { }, -1)) user2 := &model.User{ - Email: model.NewId(), - Username: model.NewId(), + Email: model.NewId(), } user2 = store.Must(ss.User().Save(user2)).(*model.User) store.Must(ss.Team().SaveMember(&model.TeamMember{ @@ -417,7 +415,6 @@ func testComplianceMessageExport(t *testing.T, ss store.Store) { assert.Equal(t, channel.DisplayName, *messageExportMap[post1.Id].ChannelDisplayName) assert.Equal(t, user1.Id, *messageExportMap[post1.Id].UserId) assert.Equal(t, user1.Email, *messageExportMap[post1.Id].UserEmail) - assert.Equal(t, user1.Username, *messageExportMap[post1.Id].Username) // post2 was made by user1 in channel1 and team1 assert.Equal(t, post2.Id, *messageExportMap[post2.Id].PostId) @@ -427,7 +424,6 @@ func testComplianceMessageExport(t *testing.T, ss store.Store) { assert.Equal(t, channel.DisplayName, *messageExportMap[post2.Id].ChannelDisplayName) assert.Equal(t, user1.Id, *messageExportMap[post2.Id].UserId) assert.Equal(t, user1.Email, *messageExportMap[post2.Id].UserEmail) - assert.Equal(t, user1.Username, *messageExportMap[post2.Id].Username) // post3 is a DM between user1 and user2 assert.Equal(t, post3.Id, *messageExportMap[post3.Id].PostId) @@ -436,5 +432,4 @@ func testComplianceMessageExport(t *testing.T, ss store.Store) { assert.Equal(t, directMessageChannel.Id, *messageExportMap[post3.Id].ChannelId) assert.Equal(t, user1.Id, *messageExportMap[post3.Id].UserId) assert.Equal(t, user1.Email, *messageExportMap[post3.Id].UserEmail) - assert.Equal(t, user1.Username, *messageExportMap[post3.Id].Username) } diff --git a/store/storetest/post_store.go b/store/storetest/post_store.go index e663d5a41..4deb7f8d4 100644 --- a/store/storetest/post_store.go +++ b/store/storetest/post_store.go @@ -27,7 +27,7 @@ func TestPostStore(t *testing.T, ss store.Store) { t.Run("PermDelete1Level", func(t *testing.T) { testPostStorePermDelete1Level(t, ss) }) t.Run("PermDelete1Level2", func(t *testing.T) { testPostStorePermDelete1Level2(t, ss) }) t.Run("GetWithChildren", func(t *testing.T) { testPostStoreGetWithChildren(t, ss) }) - t.Run("GetPostsWithDetails", func(t *testing.T) { testPostStoreGetPostsWithDetails(t, ss) }) + t.Run("GetPostsWtihDetails", func(t *testing.T) { testPostStoreGetPostsWtihDetails(t, ss) }) t.Run("GetPostsBeforeAfter", func(t *testing.T) { testPostStoreGetPostsBeforeAfter(t, ss) }) t.Run("GetPostsSince", func(t *testing.T) { testPostStoreGetPostsSince(t, ss) }) t.Run("Search", func(t *testing.T) { testPostStoreSearch(t, ss) }) @@ -490,7 +490,7 @@ func testPostStoreGetWithChildren(t *testing.T, ss store.Store) { } } -func testPostStoreGetPostsWithDetails(t *testing.T, ss store.Store) { +func testPostStoreGetPostsWtihDetails(t *testing.T, ss store.Store) { o1 := &model.Post{} o1.ChannelId = model.NewId() o1.UserId = model.NewId() @@ -591,25 +591,6 @@ func testPostStoreGetPostsWithDetails(t *testing.T, ss store.Store) { if r2.Posts[o1.Id].Message != o1.Message { t.Fatal("Missing parent") } - - // Run once to fill cache - <-ss.Post().GetPosts(o1.ChannelId, 0, 30, true) - - o6 := &model.Post{} - o6.ChannelId = o1.ChannelId - o6.UserId = model.NewId() - o6.Message = "zz" + model.NewId() + "b" - o6 = (<-ss.Post().Save(o6)).Data.(*model.Post) - - // Should only be 6 since we hit the cache - r3 := (<-ss.Post().GetPosts(o1.ChannelId, 0, 30, true)).Data.(*model.PostList) - assert.Equal(t, 6, len(r3.Order)) - - ss.Post().InvalidateLastPostTimeCache(o1.ChannelId) - - // Cache was invalidated, we should get all the posts - r4 := (<-ss.Post().GetPosts(o1.ChannelId, 0, 30, true)).Data.(*model.PostList) - assert.Equal(t, 7, len(r4.Order)) } func testPostStoreGetPostsBeforeAfter(t *testing.T, ss store.Store) { diff --git a/templates/globalrelay_compliance_export.html b/templates/globalrelay_compliance_export.html deleted file mode 100644 index 91028d11c..000000000 --- a/templates/globalrelay_compliance_export.html +++ /dev/null @@ -1,91 +0,0 @@ -{{define "globalrelay_compliance_export"}} - - -

Mattermost Compliance Export

- -

Conversation Summary

-
-
    -
  • Channel: {{.Props.ChannelName}}
  • -
  • Started: {{.Props.Started}}
  • -
  • Ended: {{.Props.Ended}}
  • -
  • Duration: {{.Props.Duration}} Minutes
  • -
-
- - - - - - - - - - {{.Props.ParticipantRows}} -
Username
JoinedLeftDurationMessages
- -

Messages

-
-
    - {{.Props.Messages}} -
-
- -

Exported on {{.Props.ExportDate}}

-{{end}} \ No newline at end of file diff --git a/templates/globalrelay_compliance_export_message.html b/templates/globalrelay_compliance_export_message.html deleted file mode 100644 index 3a47b29b7..000000000 --- a/templates/globalrelay_compliance_export_message.html +++ /dev/null @@ -1,8 +0,0 @@ -{{define "globalrelay_compliance_export_message"}} -
  • - {{.Props.SentTime}} - @{{.Props.Username}} - - {{.Props.Message}} -
  • -{{end}} diff --git a/templates/globalrelay_compliance_export_participant_row.html b/templates/globalrelay_compliance_export_participant_row.html deleted file mode 100644 index 7a61e23eb..000000000 --- a/templates/globalrelay_compliance_export_participant_row.html +++ /dev/null @@ -1,10 +0,0 @@ -{{define "globalrelay_compliance_export_participant_row"}} - - @{{.Props.Username}} - {{.Props.Email}} - {{.Props.Joined}} - {{.Props.Left}} - {{.Props.DurationMinutes}} Minutes - {{.Props.NumMessages}} - -{{end}} \ No newline at end of file diff --git a/utils/api.go b/utils/api.go index 51524074d..005c3284b 100644 --- a/utils/api.go +++ b/utils/api.go @@ -4,9 +4,6 @@ package utils import ( - "crypto" - "crypto/rand" - "encoding/base64" "fmt" "html/template" "net/http" @@ -35,25 +32,13 @@ func OriginChecker(allowedOrigins string) func(*http.Request) bool { } } -func RenderWebAppError(w http.ResponseWriter, r *http.Request, err *model.AppError, s crypto.Signer) { - RenderWebError(w, r, err.StatusCode, url.Values{ - "message": []string{err.Message}, - }, s) -} - -func RenderWebError(w http.ResponseWriter, r *http.Request, status int, params url.Values, s crypto.Signer) { - queryString := params.Encode() - - h := crypto.SHA256 - sum := h.New() - sum.Write([]byte("/error?" + queryString)) - signature, err := s.Sign(rand.Reader, sum.Sum(nil), h) - if err != nil { - http.Error(w, "", http.StatusInternalServerError) - return +func RenderWebError(err *model.AppError, w http.ResponseWriter, r *http.Request) { + status := http.StatusTemporaryRedirect + if err.StatusCode != http.StatusInternalServerError { + status = err.StatusCode } - destination := strings.TrimRight(GetSiteURL(), "/") + "/error?" + queryString + "&s=" + base64.URLEncoding.EncodeToString(signature) + destination := strings.TrimRight(GetSiteURL(), "/") + "/error?message=" + url.QueryEscape(err.Message) if status >= 300 && status < 400 { http.Redirect(w, r, destination, status) return diff --git a/utils/api_test.go b/utils/api_test.go deleted file mode 100644 index 5e41c7bfe..000000000 --- a/utils/api_test.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -package utils - -import ( - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "crypto/sha256" - "encoding/asn1" - "encoding/base64" - "math/big" - "net/http" - "net/http/httptest" - "net/url" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestRenderWebError(t *testing.T) { - r := httptest.NewRequest("GET", "http://foo", nil) - w := httptest.NewRecorder() - key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - require.NoError(t, err) - RenderWebError(w, r, http.StatusTemporaryRedirect, url.Values{ - "foo": []string{"bar"}, - }, key) - - resp := w.Result() - location, err := url.Parse(resp.Header.Get("Location")) - require.NoError(t, err) - require.NotEmpty(t, location.Query().Get("s")) - - type ecdsaSignature struct { - R, S *big.Int - } - var rs ecdsaSignature - s, err := base64.URLEncoding.DecodeString(location.Query().Get("s")) - require.NoError(t, err) - _, err = asn1.Unmarshal(s, &rs) - require.NoError(t, err) - - assert.Equal(t, "bar", location.Query().Get("foo")) - h := sha256.Sum256([]byte("/error?foo=bar")) - assert.True(t, ecdsa.Verify(&key.PublicKey, h[:], rs.R, rs.S)) -} diff --git a/utils/authorization.go b/utils/authorization.go index 42815b807..39a0d606c 100644 --- a/utils/authorization.go +++ b/utils/authorization.go @@ -7,7 +7,7 @@ import ( "github.com/mattermost/mattermost-server/model" ) -func DefaultRolesBasedOnConfig(cfg *model.Config, isLicensed bool) map[string]*model.Role { +func DefaultRolesBasedOnConfig(cfg *model.Config) map[string]*model.Role { roles := make(map[string]*model.Role) for id, role := range model.DefaultRoles { copy := &model.Role{} @@ -15,7 +15,7 @@ func DefaultRolesBasedOnConfig(cfg *model.Config, isLicensed bool) map[string]*m roles[id] = copy } - if isLicensed { + if IsLicensed() { switch *cfg.TeamSettings.RestrictPublicChannelCreation { case model.PERMISSIONS_ALL: roles[model.TEAM_USER_ROLE_ID].Permissions = append( @@ -35,7 +35,7 @@ func DefaultRolesBasedOnConfig(cfg *model.Config, isLicensed bool) map[string]*m ) } - if isLicensed { + if IsLicensed() { switch *cfg.TeamSettings.RestrictPublicChannelManagement { case model.PERMISSIONS_ALL: roles[model.TEAM_USER_ROLE_ID].Permissions = append( @@ -64,7 +64,7 @@ func DefaultRolesBasedOnConfig(cfg *model.Config, isLicensed bool) map[string]*m ) } - if isLicensed { + if IsLicensed() { switch *cfg.TeamSettings.RestrictPublicChannelDeletion { case model.PERMISSIONS_ALL: roles[model.TEAM_USER_ROLE_ID].Permissions = append( @@ -93,7 +93,7 @@ func DefaultRolesBasedOnConfig(cfg *model.Config, isLicensed bool) map[string]*m ) } - if isLicensed { + if IsLicensed() { switch *cfg.TeamSettings.RestrictPrivateChannelCreation { case model.PERMISSIONS_ALL: roles[model.TEAM_USER_ROLE_ID].Permissions = append( @@ -113,7 +113,7 @@ func DefaultRolesBasedOnConfig(cfg *model.Config, isLicensed bool) map[string]*m ) } - if isLicensed { + if IsLicensed() { switch *cfg.TeamSettings.RestrictPrivateChannelManagement { case model.PERMISSIONS_ALL: roles[model.TEAM_USER_ROLE_ID].Permissions = append( @@ -142,7 +142,7 @@ func DefaultRolesBasedOnConfig(cfg *model.Config, isLicensed bool) map[string]*m ) } - if isLicensed { + if IsLicensed() { switch *cfg.TeamSettings.RestrictPrivateChannelDeletion { case model.PERMISSIONS_ALL: roles[model.TEAM_USER_ROLE_ID].Permissions = append( @@ -172,7 +172,7 @@ func DefaultRolesBasedOnConfig(cfg *model.Config, isLicensed bool) map[string]*m } // Restrict permissions for Private Channel Manage Members - if isLicensed { + if IsLicensed() { switch *cfg.TeamSettings.RestrictPrivateChannelManageMembers { case model.PERMISSIONS_ALL: roles[model.CHANNEL_USER_ROLE_ID].Permissions = append( @@ -214,7 +214,7 @@ func DefaultRolesBasedOnConfig(cfg *model.Config, isLicensed bool) map[string]*m } // Grant permissions for inviting and adding users to a team. - if isLicensed { + if IsLicensed() { if *cfg.TeamSettings.RestrictTeamInvite == model.PERMISSIONS_TEAM_ADMIN { roles[model.TEAM_ADMIN_ROLE_ID].Permissions = append( roles[model.TEAM_ADMIN_ROLE_ID].Permissions, @@ -236,7 +236,7 @@ func DefaultRolesBasedOnConfig(cfg *model.Config, isLicensed bool) map[string]*m ) } - if isLicensed { + if IsLicensed() { switch *cfg.ServiceSettings.RestrictPostDelete { case model.PERMISSIONS_DELETE_POST_ALL: roles[model.CHANNEL_USER_ROLE_ID].Permissions = append( diff --git a/utils/config.go b/utils/config.go index a855733a7..87ebee693 100644 --- a/utils/config.go +++ b/utils/config.go @@ -342,7 +342,7 @@ func LoadConfig(fileName string) (config *model.Config, configPath string, appEr return config, configPath, nil } -func GenerateClientConfig(c *model.Config, diagnosticId string, license *model.License) map[string]string { +func GenerateClientConfig(c *model.Config, diagnosticId string) map[string]string { props := make(map[string]string) props["Version"] = model.CurrentVersion @@ -459,17 +459,18 @@ func GenerateClientConfig(c *model.Config, diagnosticId string, license *model.L hasImageProxy := c.ServiceSettings.ImageProxyType != nil && *c.ServiceSettings.ImageProxyType != "" && c.ServiceSettings.ImageProxyURL != nil && *c.ServiceSettings.ImageProxyURL != "" props["HasImageProxy"] = strconv.FormatBool(hasImageProxy) - if license != nil { + if IsLicensed() { + License := License() props["ExperimentalTownSquareIsReadOnly"] = strconv.FormatBool(*c.TeamSettings.ExperimentalTownSquareIsReadOnly) props["ExperimentalEnableAuthenticationTransfer"] = strconv.FormatBool(*c.ServiceSettings.ExperimentalEnableAuthenticationTransfer) - if *license.Features.CustomBrand { + if *License.Features.CustomBrand { props["EnableCustomBrand"] = strconv.FormatBool(*c.TeamSettings.EnableCustomBrand) props["CustomBrandText"] = *c.TeamSettings.CustomBrandText props["CustomDescriptionText"] = *c.TeamSettings.CustomDescriptionText } - if *license.Features.LDAP { + if *License.Features.LDAP { props["EnableLdap"] = strconv.FormatBool(*c.LdapSettings.Enable) props["LdapLoginFieldName"] = *c.LdapSettings.LoginFieldName props["LdapNicknameAttributeSet"] = strconv.FormatBool(*c.LdapSettings.NicknameAttribute != "") @@ -480,16 +481,16 @@ func GenerateClientConfig(c *model.Config, diagnosticId string, license *model.L props["LdapLoginButtonTextColor"] = *c.LdapSettings.LoginButtonTextColor } - if *license.Features.MFA { + if *License.Features.MFA { props["EnableMultifactorAuthentication"] = strconv.FormatBool(*c.ServiceSettings.EnableMultifactorAuthentication) props["EnforceMultifactorAuthentication"] = strconv.FormatBool(*c.ServiceSettings.EnforceMultifactorAuthentication) } - if *license.Features.Compliance { + if *License.Features.Compliance { props["EnableCompliance"] = strconv.FormatBool(*c.ComplianceSettings.Enable) } - if *license.Features.SAML { + if *License.Features.SAML { props["EnableSaml"] = strconv.FormatBool(*c.SamlSettings.Enable) props["SamlLoginButtonText"] = *c.SamlSettings.LoginButtonText props["SamlFirstNameAttributeSet"] = strconv.FormatBool(*c.SamlSettings.FirstNameAttribute != "") @@ -500,23 +501,23 @@ func GenerateClientConfig(c *model.Config, diagnosticId string, license *model.L props["SamlLoginButtonTextColor"] = *c.SamlSettings.LoginButtonTextColor } - if *license.Features.Cluster { + if *License.Features.Cluster { props["EnableCluster"] = strconv.FormatBool(*c.ClusterSettings.Enable) } - if *license.Features.Cluster { + if *License.Features.Cluster { props["EnableMetrics"] = strconv.FormatBool(*c.MetricsSettings.Enable) } - if *license.Features.GoogleOAuth { + if *License.Features.GoogleOAuth { props["EnableSignUpWithGoogle"] = strconv.FormatBool(c.GoogleSettings.Enable) } - if *license.Features.Office365OAuth { + if *License.Features.Office365OAuth { props["EnableSignUpWithOffice365"] = strconv.FormatBool(c.Office365Settings.Enable) } - if *license.Features.PasswordRequirements { + if *License.Features.PasswordRequirements { props["PasswordMinimumLength"] = fmt.Sprintf("%v", *c.PasswordSettings.MinimumLength) props["PasswordRequireLowercase"] = strconv.FormatBool(*c.PasswordSettings.Lowercase) props["PasswordRequireUppercase"] = strconv.FormatBool(*c.PasswordSettings.Uppercase) @@ -524,7 +525,7 @@ func GenerateClientConfig(c *model.Config, diagnosticId string, license *model.L props["PasswordRequireSymbol"] = strconv.FormatBool(*c.PasswordSettings.Symbol) } - if *license.Features.Announcement { + if *License.Features.Announcement { props["EnableBanner"] = strconv.FormatBool(*c.AnnouncementSettings.EnableBanner) props["BannerText"] = *c.AnnouncementSettings.BannerText props["BannerColor"] = *c.AnnouncementSettings.BannerColor @@ -532,14 +533,14 @@ func GenerateClientConfig(c *model.Config, diagnosticId string, license *model.L props["AllowBannerDismissal"] = strconv.FormatBool(*c.AnnouncementSettings.AllowBannerDismissal) } - if *license.Features.ThemeManagement { + if *License.Features.ThemeManagement { props["EnableThemeSelection"] = strconv.FormatBool(*c.ThemeSettings.EnableThemeSelection) props["DefaultTheme"] = *c.ThemeSettings.DefaultTheme props["AllowCustomThemes"] = strconv.FormatBool(*c.ThemeSettings.AllowCustomThemes) props["AllowedThemes"] = strings.Join(c.ThemeSettings.AllowedThemes, ",") } - if *license.Features.DataRetention { + if *License.Features.DataRetention { props["DataRetentionEnableMessageDeletion"] = strconv.FormatBool(*c.DataRetentionSettings.EnableMessageDeletion) props["DataRetentionMessageRetentionDays"] = strconv.FormatInt(int64(*c.DataRetentionSettings.MessageRetentionDays), 10) props["DataRetentionEnableFileDeletion"] = strconv.FormatBool(*c.DataRetentionSettings.EnableFileDeletion) diff --git a/utils/config_test.go b/utils/config_test.go index 5809422f1..9abc56d5e 100644 --- a/utils/config_test.go +++ b/utils/config_test.go @@ -197,7 +197,7 @@ func TestGetClientConfig(t *testing.T) { cfg, _, err := LoadConfig("config.json") require.Nil(t, err) - configMap := GenerateClientConfig(cfg, "", nil) + configMap := GenerateClientConfig(cfg, "") if configMap["EmailNotificationContentsType"] != *cfg.EmailSettings.EmailNotificationContentsType { t.Fatal("EmailSettings.EmailNotificationContentsType not exposed to client config") } diff --git a/utils/file_backend.go b/utils/file_backend.go index 42af7f604..c7a6c5591 100644 --- a/utils/file_backend.go +++ b/utils/file_backend.go @@ -22,7 +22,7 @@ type FileBackend interface { RemoveDirectory(path string) *model.AppError } -func NewFileBackend(settings *model.FileSettings, enableComplianceFeatures bool) (FileBackend, *model.AppError) { +func NewFileBackend(settings *model.FileSettings) (FileBackend, *model.AppError) { switch *settings.DriverName { case model.IMAGE_DRIVER_S3: return &S3FileBackend{ @@ -33,7 +33,7 @@ func NewFileBackend(settings *model.FileSettings, enableComplianceFeatures bool) signV2: settings.AmazonS3SignV2 != nil && *settings.AmazonS3SignV2, region: settings.AmazonS3Region, bucket: settings.AmazonS3Bucket, - encrypt: settings.AmazonS3SSE != nil && *settings.AmazonS3SSE && enableComplianceFeatures, + encrypt: settings.AmazonS3SSE != nil && *settings.AmazonS3SSE && IsLicensed() && *License().Features.Compliance, trace: settings.AmazonS3Trace != nil && *settings.AmazonS3Trace, }, nil case model.IMAGE_DRIVER_LOCAL: diff --git a/utils/file_backend_test.go b/utils/file_backend_test.go index 46f75574e..76cd1f4a8 100644 --- a/utils/file_backend_test.go +++ b/utils/file_backend_test.go @@ -63,7 +63,7 @@ func TestS3FileBackendTestSuite(t *testing.T) { func (s *FileBackendTestSuite) SetupTest() { TranslationsPreInit() - backend, err := NewFileBackend(&s.settings, true) + backend, err := NewFileBackend(&s.settings) require.Nil(s.T(), err) s.backend = backend } diff --git a/utils/html.go b/utils/html.go index 6bbe55c6d..02db8c97a 100644 --- a/utils/html.go +++ b/utils/html.go @@ -23,7 +23,7 @@ type HTMLTemplateWatcher struct { func NewHTMLTemplateWatcher(directory string) (*HTMLTemplateWatcher, error) { templatesDir, _ := FindDir(directory) - l4g.Debug("Parsing server templates at %v", templatesDir) + l4g.Debug(T("api.api.init.parsing_templates.debug"), templatesDir) ret := &HTMLTemplateWatcher{ stop: make(chan struct{}), @@ -55,15 +55,15 @@ func NewHTMLTemplateWatcher(directory string) (*HTMLTemplateWatcher, error) { return case event := <-watcher.Events: if event.Op&fsnotify.Write == fsnotify.Write { - l4g.Info("Re-parsing templates because of modified file %v", event.Name) + l4g.Info(T("web.reparse_templates.info"), event.Name) if htmlTemplates, err := template.ParseGlob(templatesDir + "*.html"); err != nil { - l4g.Error("Failed to parse templates %v", err) + l4g.Error(T("web.parsing_templates.error"), err) } else { ret.templates.Store(htmlTemplates) } } case err := <-watcher.Errors: - l4g.Error("Failed in directory watcher %s", err) + l4g.Error(T("web.dir_fail.error"), err) } } }() diff --git a/utils/inbucket.go b/utils/inbucket.go index 5c40d5757..46011989b 100644 --- a/utils/inbucket.go +++ b/utils/inbucket.go @@ -4,7 +4,6 @@ package utils import ( - "bytes" "encoding/json" "fmt" "io" @@ -38,12 +37,6 @@ type JSONMessageInbucket struct { Text string HTML string `json:"Html"` } - Attachments []struct { - Filename string - ContentType string `json:"content-type"` - DownloadLink string `json:"download-link"` - Bytes []byte `json:"-"` - } } func ParseEmail(email string) string { @@ -96,54 +89,21 @@ func GetMessageFromMailbox(email, id string) (results JSONMessageInbucket, err e var record JSONMessageInbucket url := fmt.Sprintf("%s%s%s/%s", getInbucketHost(), INBUCKET_API, parsedEmail, id) - emailResponse, err := get(url) - if err != nil { - return record, err - } - defer emailResponse.Body.Close() - - err = json.NewDecoder(emailResponse.Body).Decode(&record) - - // download attachments - if record.Attachments != nil && len(record.Attachments) > 0 { - for i := range record.Attachments { - if bytes, err := downloadAttachment(record.Attachments[i].DownloadLink); err != nil { - return record, err - } else { - record.Attachments[i].Bytes = make([]byte, len(bytes)) - copy(record.Attachments[i].Bytes, bytes) - } - } - } - - return record, err -} - -func downloadAttachment(url string) ([]byte, error) { - attachmentResponse, err := get(url) - if err != nil { - return nil, err - } - defer attachmentResponse.Body.Close() - - buf := new(bytes.Buffer) - io.Copy(buf, attachmentResponse.Body) - return buf.Bytes(), nil -} - -func get(url string) (*http.Response, error) { req, err := http.NewRequest("GET", url, nil) if err != nil { - return nil, err + return record, err } client := &http.Client{} + resp, err := client.Do(req) if err != nil { - return nil, err + return record, err } + defer resp.Body.Close() - return resp, nil + err = json.NewDecoder(resp.Body).Decode(&record) + return record, err } func DeleteMailBox(email string) (err error) { diff --git a/utils/license.go b/utils/license.go index 2853a58d0..2aaa2a549 100644 --- a/utils/license.go +++ b/utils/license.go @@ -5,21 +5,28 @@ package utils import ( "crypto" + "crypto/md5" "crypto/rsa" "crypto/sha512" "crypto/x509" "encoding/base64" "encoding/pem" + "fmt" "io/ioutil" "os" "strconv" "strings" + "sync/atomic" l4g "github.com/alecthomas/log4go" "github.com/mattermost/mattermost-server/model" ) +var isLicensedInt32 int32 +var licenseValue atomic.Value +var clientLicenseValue atomic.Value + var publicKey []byte = []byte(`-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyZmShlU8Z8HdG0IWSZ8r tSyzyxrXkJjsFUf0Ke7bm/TLtIggRdqOcUF3XEWqQk5RGD5vuq7Rlg1zZqMEBk8N @@ -30,6 +37,92 @@ a0v85XL6i9ote2P+fLZ3wX9EoioHzgdgB7arOxY50QRJO7OyCqpKFKv6lRWTXuSt hwIDAQAB -----END PUBLIC KEY-----`) +func init() { + SetLicense(nil) +} + +func IsLicensed() bool { + return atomic.LoadInt32(&isLicensedInt32) == 1 +} + +func SetIsLicensed(v bool) { + if v { + atomic.StoreInt32(&isLicensedInt32, 1) + } else { + atomic.StoreInt32(&isLicensedInt32, 0) + } +} + +func License() *model.License { + return licenseValue.Load().(*model.License) +} + +func SetClientLicense(m map[string]string) { + clientLicenseValue.Store(m) +} + +func ClientLicense() map[string]string { + return clientLicenseValue.Load().(map[string]string) +} + +func LoadLicense(licenseBytes []byte) { + if success, licenseStr := ValidateLicense(licenseBytes); success { + license := model.LicenseFromJson(strings.NewReader(licenseStr)) + SetLicense(license) + return + } + + l4g.Warn(T("utils.license.load_license.invalid.warn")) +} + +var licenseListeners = map[string]func(){} + +func AddLicenseListener(listener func()) string { + id := model.NewId() + licenseListeners[id] = listener + return id +} + +func RemoveLicenseListener(id string) { + delete(licenseListeners, id) +} + +func SetLicense(license *model.License) bool { + defer func() { + for _, listener := range licenseListeners { + listener() + } + }() + + if license == nil { + SetIsLicensed(false) + license = &model.License{ + Features: new(model.Features), + } + license.Features.SetDefaults() + licenseValue.Store(license) + + SetClientLicense(map[string]string{"IsLicensed": "false"}) + + return false + } else { + license.Features.SetDefaults() + + if !license.IsExpired() { + licenseValue.Store(license) + SetIsLicensed(true) + clientLicenseValue.Store(getClientLicense(license)) + return true + } + + return false + } +} + +func RemoveLicense() { + SetLicense(nil) +} + func ValidateLicense(signed []byte) (bool, string) { decoded := make([]byte, base64.StdEncoding.DecodedLen(len(signed))) @@ -120,12 +213,12 @@ func GetLicenseFileLocation(fileLocation string) string { } } -func GetClientLicense(l *model.License) map[string]string { +func getClientLicense(l *model.License) map[string]string { props := make(map[string]string) - props["IsLicensed"] = strconv.FormatBool(l != nil) + props["IsLicensed"] = strconv.FormatBool(IsLicensed()) - if l != nil { + if IsLicensed() { props["Id"] = l.Id props["Users"] = strconv.Itoa(*l.Features.Users) props["LDAP"] = strconv.FormatBool(*l.Features.LDAP) @@ -155,3 +248,39 @@ func GetClientLicense(l *model.License) map[string]string { return props } + +func GetClientLicenseEtag(useSanitized bool) string { + value := "" + + lic := ClientLicense() + + if useSanitized { + lic = GetSanitizedClientLicense() + } + + for k, v := range lic { + value += fmt.Sprintf("%s:%s;", k, v) + } + + return model.Etag(fmt.Sprintf("%x", md5.Sum([]byte(value)))) +} + +func GetSanitizedClientLicense() map[string]string { + sanitizedLicense := make(map[string]string) + + for k, v := range ClientLicense() { + sanitizedLicense[k] = v + } + + if IsLicensed() { + delete(sanitizedLicense, "Id") + delete(sanitizedLicense, "Name") + delete(sanitizedLicense, "Email") + delete(sanitizedLicense, "PhoneNumber") + delete(sanitizedLicense, "IssuedAt") + delete(sanitizedLicense, "StartsAt") + delete(sanitizedLicense, "ExpiresAt") + } + + return sanitizedLicense +} diff --git a/utils/license_test.go b/utils/license_test.go index c2d1b4c05..9771ec497 100644 --- a/utils/license_test.go +++ b/utils/license_test.go @@ -5,20 +5,87 @@ package utils import ( "testing" + + "github.com/mattermost/mattermost-server/model" ) +func TestSetLicense(t *testing.T) { + l1 := &model.License{} + l1.Features = &model.Features{} + l1.Customer = &model.Customer{} + l1.StartsAt = model.GetMillis() - 1000 + l1.ExpiresAt = model.GetMillis() + 100000 + if ok := SetLicense(l1); !ok { + t.Fatal("license should have worked") + } + + l2 := &model.License{} + l2.Features = &model.Features{} + l2.Customer = &model.Customer{} + l2.StartsAt = model.GetMillis() - 1000 + l2.ExpiresAt = model.GetMillis() - 100 + if ok := SetLicense(l2); ok { + t.Fatal("license should have failed") + } + + l3 := &model.License{} + l3.Features = &model.Features{} + l3.Customer = &model.Customer{} + l3.StartsAt = model.GetMillis() + 10000 + l3.ExpiresAt = model.GetMillis() + 100000 + if ok := SetLicense(l3); !ok { + t.Fatal("license should have passed") + } +} + func TestValidateLicense(t *testing.T) { b1 := []byte("junk") if ok, _ := ValidateLicense(b1); ok { t.Fatal("should have failed - bad license") } + LoadLicense(b1) + b2 := []byte("junkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunkjunk") if ok, _ := ValidateLicense(b2); ok { t.Fatal("should have failed - bad license") } } +func TestClientLicenseEtag(t *testing.T) { + etag1 := GetClientLicenseEtag(false) + + SetClientLicense(map[string]string{"SomeFeature": "true", "IsLicensed": "true"}) + + etag2 := GetClientLicenseEtag(false) + if etag1 == etag2 { + t.Fatal("etags should not match") + } + + SetClientLicense(map[string]string{"SomeFeature": "true", "IsLicensed": "false"}) + + etag3 := GetClientLicenseEtag(false) + if etag2 == etag3 { + t.Fatal("etags should not match") + } +} + +func TestGetSanitizedClientLicense(t *testing.T) { + l1 := &model.License{} + l1.Features = &model.Features{} + l1.Customer = &model.Customer{} + l1.Customer.Name = "TestName" + l1.StartsAt = model.GetMillis() - 1000 + l1.ExpiresAt = model.GetMillis() + 100000 + SetLicense(l1) + + m := GetSanitizedClientLicense() + + if _, ok := m["Name"]; ok { + t.Fatal("should have been sanatized") + } +} + func TestGetLicenseFileLocation(t *testing.T) { fileName := GetLicenseFileLocation("") if len(fileName) == 0 { diff --git a/utils/mail.go b/utils/mail.go index 633f97818..b0289da5e 100644 --- a/utils/mail.go +++ b/utils/mail.go @@ -15,8 +15,6 @@ import ( "net/http" - "io" - l4g "github.com/alecthomas/log4go" "github.com/mattermost/html2text" "github.com/mattermost/mattermost-server/model" @@ -105,73 +103,37 @@ func TestConnection(config *model.Config) { defer c.Close() } -func SendMailUsingConfig(to, subject, htmlBody string, config *model.Config, enableComplianceFeatures bool) *model.AppError { - fromMail := mail.Address{Name: config.EmailSettings.FeedbackName, Address: config.EmailSettings.FeedbackEmail} - return sendMail(to, to, fromMail, subject, htmlBody, nil, nil, config, enableComplianceFeatures) -} - -// allows for sending an email with attachments and differing MIME/SMTP recipients -func SendMailUsingConfigAdvanced(mimeTo, smtpTo string, from mail.Address, subject, htmlBody string, attachments []*model.FileInfo, mimeHeaders map[string]string, config *model.Config, enableComplianceFeatures bool) *model.AppError { - return sendMail(mimeTo, smtpTo, from, subject, htmlBody, attachments, mimeHeaders, config, enableComplianceFeatures) -} - -func sendMail(mimeTo, smtpTo string, from mail.Address, subject, htmlBody string, attachments []*model.FileInfo, mimeHeaders map[string]string, config *model.Config, enableComplianceFeatures bool) *model.AppError { +func SendMailUsingConfig(to, subject, htmlBody string, config *model.Config) *model.AppError { if !config.EmailSettings.SendEmailNotifications || len(config.EmailSettings.SMTPServer) == 0 { return nil } - l4g.Debug(T("utils.mail.send_mail.sending.debug"), mimeTo, subject) + l4g.Debug(T("utils.mail.send_mail.sending.debug"), to, subject) htmlMessage := "\r\n" + htmlBody + "" + fromMail := mail.Address{Name: config.EmailSettings.FeedbackName, Address: config.EmailSettings.FeedbackEmail} + txtBody, err := html2text.FromString(htmlBody) if err != nil { l4g.Warn(err) txtBody = "" } - headers := map[string][]string{ - "From": {from.String()}, - "To": {mimeTo}, + m := gomail.NewMessage(gomail.SetCharset("UTF-8")) + m.SetHeaders(map[string][]string{ + "From": {fromMail.String()}, + "To": {to}, "Subject": {encodeRFC2047Word(subject)}, "Content-Transfer-Encoding": {"8bit"}, "Auto-Submitted": {"auto-generated"}, "Precedence": {"bulk"}, - } - if mimeHeaders != nil { - for k, v := range mimeHeaders { - headers[k] = []string{encodeRFC2047Word(v)} - } - } - - m := gomail.NewMessage(gomail.SetCharset("UTF-8")) - m.SetHeaders(headers) + }) m.SetDateHeader("Date", time.Now()) + m.SetBody("text/plain", txtBody) m.AddAlternative("text/html", htmlMessage) - if attachments != nil { - fileBackend, err := NewFileBackend(&config.FileSettings, enableComplianceFeatures) - if err != nil { - return err - } - - for _, fileInfo := range attachments { - m.Attach(fileInfo.Name, gomail.SetCopyFunc(func(writer io.Writer) error { - bytes, err := fileBackend.ReadFile(fileInfo.Path) - if err != nil { - return err - } - if _, err := writer.Write(bytes); err != nil { - return model.NewAppError("SendMail", "utils.mail.sendMail.attachments.write_error", nil, err.Error(), http.StatusInternalServerError) - } - return nil - })) - - } - - } - conn, err1 := connectToSMTPServer(config) if err1 != nil { return err1 @@ -185,11 +147,11 @@ func sendMail(mimeTo, smtpTo string, from mail.Address, subject, htmlBody string defer c.Quit() defer c.Close() - if err := c.Mail(from.Address); err != nil { + if err := c.Mail(fromMail.Address); err != nil { return model.NewAppError("SendMail", "utils.mail.send_mail.from_address.app_error", nil, err.Error(), http.StatusInternalServerError) } - if err := c.Rcpt(smtpTo); err != nil { + if err := c.Rcpt(to); err != nil { return model.NewAppError("SendMail", "utils.mail.send_mail.to_address.app_error", nil, err.Error(), http.StatusInternalServerError) } diff --git a/utils/mail_test.go b/utils/mail_test.go index 703420441..574f71f46 100644 --- a/utils/mail_test.go +++ b/utils/mail_test.go @@ -7,10 +7,6 @@ import ( "strings" "testing" - "net/mail" - - "github.com/mattermost/mattermost-server/model" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -43,18 +39,18 @@ func TestSendMailUsingConfig(t *testing.T) { require.Nil(t, err) T = GetUserTranslations("en") - var emailTo = "test@example.com" - var emailSubject = "Testing this email" - var emailBody = "This is a test from autobot" + var emailTo string = "test@example.com" + var emailSubject string = "Testing this email" + var emailBody string = "This is a test from autobot" //Delete all the messages before check the sample email DeleteMailBox(emailTo) - if err := SendMailUsingConfig(emailTo, emailSubject, emailBody, cfg, true); err != nil { + if err := SendMailUsingConfig(emailTo, emailSubject, emailBody, cfg); err != nil { t.Log(err) t.Fatal("Should connect to the STMP Server") } else { - //Check if the email was send to the right email address + //Check if the email was send to the rigth email address var resultsMailbox JSONMessageHeaderInbucket err := RetryInbucket(5, func() error { var err error @@ -79,78 +75,3 @@ func TestSendMailUsingConfig(t *testing.T) { } } } - -func TestSendMailUsingConfigAdvanced(t *testing.T) { - cfg, _, err := LoadConfig("config.json") - require.Nil(t, err) - T = GetUserTranslations("en") - - var mimeTo = "test@example.com" - var smtpTo = "test2@example.com" - var from = mail.Address{Name: "Nobody", Address: "nobody@mattermost.com"} - var emailSubject = "Testing this email" - var emailBody = "This is a test from autobot" - - //Delete all the messages before check the sample email - DeleteMailBox(smtpTo) - - // create a file that will be attached to the email - fileBackend, err := NewFileBackend(&cfg.FileSettings, true) - assert.Nil(t, err) - fileContents := []byte("hello world") - fileName := "file.txt" - assert.Nil(t, fileBackend.WriteFile(fileContents, fileName)) - defer fileBackend.RemoveFile(fileName) - - attachments := make([]*model.FileInfo, 1) - attachments[0] = &model.FileInfo{ - Name: fileName, - Path: fileName, - } - - headers := make(map[string]string) - headers["TestHeader"] = "TestValue" - - if err := SendMailUsingConfigAdvanced(mimeTo, smtpTo, from, emailSubject, emailBody, attachments, headers, cfg, true); err != nil { - t.Log(err) - t.Fatal("Should connect to the STMP Server") - } else { - //Check if the email was send to the right email address - var resultsMailbox JSONMessageHeaderInbucket - err := RetryInbucket(5, func() error { - var err error - resultsMailbox, err = GetMailBox(smtpTo) - return err - }) - if err != nil { - t.Log(err) - t.Fatal("No emails found for address " + smtpTo) - } - if err == nil && len(resultsMailbox) > 0 { - if !strings.ContainsAny(resultsMailbox[0].To[0], smtpTo) { - t.Fatal("Wrong To recipient") - } else { - if resultsEmail, err := GetMessageFromMailbox(smtpTo, resultsMailbox[0].ID); err == nil { - if !strings.Contains(resultsEmail.Body.Text, emailBody) { - t.Log(resultsEmail.Body.Text) - t.Fatal("Received message") - } - - // verify that the To header of the email message is set to the MIME recipient, even though we got it out of the SMTP recipient's email inbox - assert.Equal(t, mimeTo, resultsEmail.Header["To"][0]) - - // verify that the MIME from address is correct - unfortunately, we can't verify the SMTP from address - assert.Equal(t, from.String(), resultsEmail.Header["From"][0]) - - // check that the custom mime headers came through - header case seems to get mutated - assert.Equal(t, "TestValue", resultsEmail.Header["Testheader"][0]) - - // ensure that the attachment was successfully sent - assert.Len(t, resultsEmail.Attachments, 1) - assert.Equal(t, fileName, resultsEmail.Attachments[0].Filename) - assert.Equal(t, fileContents, resultsEmail.Attachments[0].Bytes) - } - } - } - } -} diff --git a/web/web.go b/web/web.go index e0edd1b7a..321d83a75 100644 --- a/web/web.go +++ b/web/web.go @@ -94,7 +94,7 @@ func root(c *api.Context, w http.ResponseWriter, r *http.Request) { } if api.IsApiCall(r) { - api.Handle404(c.App, w, r) + api.Handle404(w, r) return } diff --git a/web/web_test.go b/web/web_test.go index c8d64c61d..21a7968b3 100644 --- a/web/web_test.go +++ b/web/web_test.go @@ -44,10 +44,7 @@ func Setup() *app.App { } prevListenAddress := *a.Config().ServiceSettings.ListenAddress a.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = ":0" }) - serverErr := a.StartServer() - if serverErr != nil { - panic(serverErr) - } + a.StartServer() a.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = prevListenAddress }) api4.Init(a, a.Srv.Router, false) api3 := api.Init(a, a.Srv.Router) -- cgit v1.2.3-1-g7c22