summaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
authorChris <ccbrown112@gmail.com>2018-02-12 18:36:39 -0600
committerGitHub <noreply@github.com>2018-02-12 18:36:39 -0600
commitfbef16f8630f74248157c2cd9e546ece355c869a (patch)
tree95feb1a0b94f43fd38533adc508df2daf823ea86 /app
parent56f49cf4860cfca51e852ec3e7d9df772d2b2060 (diff)
parent32c1f7be239ddb19d6c59b114d9ae1a543f8ba9c (diff)
downloadchat-fbef16f8630f74248157c2cd9e546ece355c869a.tar.gz
chat-fbef16f8630f74248157c2cd9e546ece355c869a.tar.bz2
chat-fbef16f8630f74248157c2cd9e546ece355c869a.zip
Merge branch 'release-4.7' into rm-willnorris-proxy-support
Diffstat (limited to 'app')
-rw-r--r--app/admin.go3
-rw-r--r--app/app.go56
-rw-r--r--app/app_test.go3
-rw-r--r--app/apptestlib.go11
-rw-r--r--app/channel.go30
-rw-r--r--app/channel_test.go4
-rw-r--r--app/config.go98
-rw-r--r--app/config_test.go9
-rw-r--r--app/diagnostics.go1
-rw-r--r--app/email.go3
-rw-r--r--app/file.go3
-rw-r--r--app/license.go107
-rw-r--r--app/license_test.go75
-rw-r--r--app/plugin.go1
-rw-r--r--app/post.go54
-rw-r--r--app/post_test.go92
-rw-r--r--app/role.go6
-rw-r--r--app/server.go12
-rw-r--r--app/server_test.go50
-rw-r--r--app/session_test.go22
20 files changed, 213 insertions, 427 deletions
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/post.go b/app/post.go
index 30a627d3d..a541797fa 100644
--- a/app/post.go
+++ b/app/post.go
@@ -10,6 +10,7 @@ import (
"encoding/json"
"fmt"
"net/http"
+ "net/url"
"regexp"
"strings"
@@ -732,23 +733,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())
}
+ makeOpenGraphURLsAbsolute(og, requestURL)
+
return og
}
+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
+ }
+
+ 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
+ }
+
+ return parsedRequestURL.ResolveReference(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)
+ }
+}
+
func (a *App) DoPostAction(postId string, actionId string, userId string) *model.AppError {
pchan := a.Srv.Store.Post().GetSingle(postId)
diff --git a/app/post_test.go b/app/post_test.go
index cdb612195..ebe973270 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"
@@ -254,3 +256,93 @@ 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: `
+ <html>
+ <head>
+ <meta property="og:url" content="https://example.com/apps/mattermost">
+ <meta property="og:image" content="https://images.example.com/image.png">
+ </head>
+ </html>`,
+ RequestURL: "https://example.com",
+ URL: "https://example.com/apps/mattermost",
+ ImageURL: "https://images.example.com/image.png",
+ },
+ "URLs starting with /": {
+ HTML: `
+ <html>
+ <head>
+ <meta property="og:url" content="/apps/mattermost">
+ <meta property="og:image" content="/image.png">
+ </head>
+ </html>`,
+ RequestURL: "http://example.com",
+ URL: "http://example.com/apps/mattermost",
+ ImageURL: "http://example.com/image.png",
+ },
+ "HTTPS URLs starting with /": {
+ HTML: `
+ <html>
+ <head>
+ <meta property="og:url" content="/apps/mattermost">
+ <meta property="og:image" content="/image.png">
+ </head>
+ </html>`,
+ RequestURL: "https://example.com",
+ URL: "https://example.com/apps/mattermost",
+ ImageURL: "https://example.com/image.png",
+ },
+ "missing image URL": {
+ HTML: `
+ <html>
+ <head>
+ <meta property="og:url" content="/apps/mattermost">
+ </head>
+ </html>`,
+ RequestURL: "http://example.com",
+ URL: "http://example.com/apps/mattermost",
+ ImageURL: "",
+ },
+ "relative URLs": {
+ HTML: `
+ <html>
+ <head>
+ <meta property="og:url" content="index.html">
+ <meta property="og:image" content="../resources/image.png">
+ </head>
+ </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()
+ if err := og.ProcessHTML(strings.NewReader(tc.HTML)); err != nil {
+ t.Fatal(err)
+ }
+
+ 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.Fatalf("missing image url, expected %v, got nothing", tc.ImageURL)
+ }
+ })
+ }
+} \ No newline at end of file
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 })