Remove license globals entirely (#8229)
* remove license globals entirely * fix infinite recursion * test fix
Этот коммит содержится в:
@@ -237,7 +237,8 @@ func (a *App) TestEmail(userId string, cfg *model.Config) *model.AppError {
|
||||
return err
|
||||
} else {
|
||||
T := utils.GetUserTranslations(user.Locale)
|
||||
if err := utils.SendMailUsingConfig(user.Email, T("api.admin.test_email.subject"), T("api.admin.test_email.body"), cfg); err != nil {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
24
app/app.go
24
app/app.go
@@ -59,6 +59,10 @@ 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
|
||||
@@ -88,18 +92,16 @@ func New(options ...Option) (*App, error) {
|
||||
panic("Only one App should exist at a time. Did you forget to call Shutdown()?")
|
||||
}
|
||||
|
||||
// TODO: remove this once utils global license state is eliminated
|
||||
utils.SetLicense(nil)
|
||||
|
||||
app := &App{
|
||||
goroutineExitSignal: make(chan struct{}, 1),
|
||||
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),
|
||||
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(){},
|
||||
}
|
||||
|
||||
for _, option := range options {
|
||||
@@ -123,9 +125,9 @@ func New(options ...Option) (*App, error) {
|
||||
app.configListenerId = app.AddConfigListener(func(_, _ *model.Config) {
|
||||
app.configOrLicenseListener()
|
||||
})
|
||||
app.licenseListenerId = utils.AddLicenseListener(app.configOrLicenseListener)
|
||||
app.licenseListenerId = app.AddLicenseListener(app.configOrLicenseListener)
|
||||
app.regenerateClientConfig()
|
||||
app.SetDefaultRolesBasedOnConfig()
|
||||
app.setDefaultRolesBasedOnConfig()
|
||||
|
||||
l4g.Info(utils.T("api.server.new_server.init.info"))
|
||||
|
||||
@@ -166,7 +168,7 @@ func New(options ...Option) (*App, error) {
|
||||
|
||||
func (a *App) configOrLicenseListener() {
|
||||
a.regenerateClientConfig()
|
||||
a.SetDefaultRolesBasedOnConfig()
|
||||
a.setDefaultRolesBasedOnConfig()
|
||||
}
|
||||
|
||||
func (a *App) Shutdown() {
|
||||
@@ -188,7 +190,7 @@ func (a *App) Shutdown() {
|
||||
}
|
||||
|
||||
a.RemoveConfigListener(a.configListenerId)
|
||||
utils.RemoveLicenseListener(a.licenseListenerId)
|
||||
a.RemoveLicenseListener(a.licenseListenerId)
|
||||
l4g.Info(utils.T("api.server.stop_server.stopped.info"))
|
||||
|
||||
a.DisableConfigWatch()
|
||||
|
||||
@@ -106,9 +106,10 @@ func setupTestHelper(enterprise bool) *TestHelper {
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.EnableOpenServer = true })
|
||||
|
||||
utils.SetIsLicensed(enterprise)
|
||||
if enterprise {
|
||||
utils.License().Features.SetDefaults()
|
||||
th.App.SetLicense(model.NewTestLicense())
|
||||
} else {
|
||||
th.App.SetLicense(nil)
|
||||
}
|
||||
|
||||
return th
|
||||
|
||||
@@ -201,7 +201,7 @@ func (a *App) AsymmetricSigningKey() *ecdsa.PrivateKey {
|
||||
}
|
||||
|
||||
func (a *App) regenerateClientConfig() {
|
||||
a.clientConfig = utils.GenerateClientConfig(a.Config(), a.DiagnosticId())
|
||||
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)
|
||||
|
||||
@@ -316,5 +316,6 @@ func (a *App) NewEmailTemplate(name, locale string) *utils.HTMLTemplate {
|
||||
}
|
||||
|
||||
func (a *App) SendMail(to, subject, htmlBody string) *model.AppError {
|
||||
return utils.SendMailUsingConfig(to, subject, htmlBody, a.Config())
|
||||
license := a.License()
|
||||
return utils.SendMailUsingConfig(to, subject, htmlBody, a.Config(), license != nil && *license.Features.Compliance)
|
||||
}
|
||||
|
||||
@@ -58,7 +58,8 @@ const (
|
||||
)
|
||||
|
||||
func (a *App) FileBackend() (utils.FileBackend, *model.AppError) {
|
||||
return utils.NewFileBackend(&a.Config().FileSettings)
|
||||
license := a.License()
|
||||
return utils.NewFileBackend(&a.Config().FileSettings, license != nil && *license.Features.Compliance)
|
||||
}
|
||||
|
||||
func (a *App) ReadFile(path string) ([]byte, *model.AppError) {
|
||||
|
||||
105
app/license.go
105
app/license.go
@@ -4,16 +4,19 @@
|
||||
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() {
|
||||
utils.RemoveLicense()
|
||||
a.SetLicense(nil)
|
||||
|
||||
licenseId := ""
|
||||
if result := <-a.Srv.Store.System().Get(); result.Err == nil {
|
||||
@@ -36,7 +39,7 @@ func (a *App) LoadLicense() {
|
||||
|
||||
if result := <-a.Srv.Store.License().Get(licenseId); result.Err == nil {
|
||||
record := result.Data.(*model.LicenseRecord)
|
||||
utils.LoadLicense([]byte(record.Bytes))
|
||||
a.ValidateAndSetLicenseBytes([]byte(record.Bytes))
|
||||
l4g.Info("License key valid unlocking enterprise features.")
|
||||
} else {
|
||||
l4g.Info(utils.T("mattermost.load_license.find.warn"))
|
||||
@@ -104,33 +107,113 @@ func (a *App) SaveLicense(licenseBytes []byte) (*model.License, *model.AppError)
|
||||
|
||||
// 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
|
||||
license, _ := a.licenseValue.Load().(*model.License)
|
||||
return license
|
||||
}
|
||||
|
||||
func (a *App) SetLicense(license *model.License) bool {
|
||||
ok := utils.SetLicense(license)
|
||||
a.SetDefaultRolesBasedOnConfig()
|
||||
return ok
|
||||
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 {
|
||||
utils.RemoveLicense()
|
||||
if license, _ := a.licenseValue.Load().(*model.License); license == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
//"github.com/mattermost/mattermost-server/model"
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
func TestLoadLicense(t *testing.T) {
|
||||
@@ -37,3 +38,75 @@ 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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,8 +12,6 @@ func (a *App) Role(id string) *model.Role {
|
||||
return a.roles[id]
|
||||
}
|
||||
|
||||
// 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())
|
||||
func (a *App) setDefaultRolesBasedOnConfig() {
|
||||
a.roles = utils.DefaultRolesBasedOnConfig(a.Config(), a.License() != nil)
|
||||
}
|
||||
|
||||
@@ -6,11 +6,10 @@ 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) {
|
||||
@@ -111,7 +110,7 @@ func TestGetSessionIdleTimeoutInMinutes(t *testing.T) {
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Test regular session with license off, should not timeout
|
||||
*utils.License().Features.Compliance = false
|
||||
th.App.SetLicense(nil)
|
||||
|
||||
session = &model.Session{
|
||||
UserId: model.NewId(),
|
||||
@@ -125,7 +124,7 @@ func TestGetSessionIdleTimeoutInMinutes(t *testing.T) {
|
||||
_, err = th.App.GetSession(session.Token)
|
||||
assert.Nil(t, err)
|
||||
|
||||
*utils.License().Features.Compliance = true
|
||||
th.App.SetLicense(model.NewTestLicense("compliance"))
|
||||
|
||||
// Test regular session with timeout set to 0, should not timeout
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.SessionIdleTimeoutInMinutes = 0 })
|
||||
|
||||
Ссылка в новой задаче
Block a user