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(-) (limited to 'app') 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(-) (limited to 'app') 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(-) (limited to 'app') 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 --- 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 ++++++++--- 18 files changed, 71 insertions(+), 423 deletions(-) delete mode 100644 app/server_test.go (limited to 'app') 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 }) -- cgit v1.2.3-1-g7c22