MM-14439: experimental restrict system admin (#10414)
* api4: break out license and config from system * app: move some config functions from admin.go to config.go * add ExperimentalSettings.RestrictSystemAdmin * forbid various actions to restricted system admin * update default.json * fix function names in errors
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
200cfdd4a7
Коммит
9ef8c1e8b1
@@ -221,6 +221,8 @@ func Init(configservice configservice.ConfigService, globalOptionsFunc app.AppOp
|
|||||||
api.InitPost()
|
api.InitPost()
|
||||||
api.InitFile()
|
api.InitFile()
|
||||||
api.InitSystem()
|
api.InitSystem()
|
||||||
|
api.InitLicense()
|
||||||
|
api.InitConfig()
|
||||||
api.InitWebhook()
|
api.InitWebhook()
|
||||||
api.InitPreference()
|
api.InitPreference()
|
||||||
api.InitSaml()
|
api.InitSaml()
|
||||||
|
|||||||
@@ -19,6 +19,11 @@ func getClusterStatus(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if *c.App.Config().ExperimentalSettings.RestrictSystemAdmin {
|
||||||
|
c.Err = model.NewAppError("getClusterStatus", "api.restricted_system_admin", nil, "", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
infos := c.App.GetClusterStatus()
|
infos := c.App.GetClusterStatus()
|
||||||
w.Write([]byte(model.ClusterInfosToJson(infos)))
|
w.Write([]byte(model.ClusterInfosToJson(infos)))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,19 +5,32 @@ package api4
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/mattermost/mattermost-server/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetClusterStatus(t *testing.T) {
|
func TestGetClusterStatus(t *testing.T) {
|
||||||
th := Setup().InitBasic()
|
th := Setup().InitBasic()
|
||||||
defer th.TearDown()
|
defer th.TearDown()
|
||||||
|
|
||||||
_, resp := th.Client.GetClusterStatus()
|
t.Run("as system user", func(t *testing.T) {
|
||||||
CheckForbiddenStatus(t, resp)
|
_, resp := th.Client.GetClusterStatus()
|
||||||
|
CheckForbiddenStatus(t, resp)
|
||||||
|
})
|
||||||
|
|
||||||
infos, resp := th.SystemAdminClient.GetClusterStatus()
|
t.Run("as system admin", func(t *testing.T) {
|
||||||
CheckNoError(t, resp)
|
infos, resp := th.SystemAdminClient.GetClusterStatus()
|
||||||
|
CheckNoError(t, resp)
|
||||||
|
|
||||||
if infos == nil {
|
if infos == nil {
|
||||||
t.Fatal("should not be nil")
|
t.Fatal("should not be nil")
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("as restricted system admin", func(t *testing.T) {
|
||||||
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ExperimentalSettings.RestrictSystemAdmin = true })
|
||||||
|
|
||||||
|
_, resp := th.SystemAdminClient.GetClusterStatus()
|
||||||
|
CheckForbiddenStatus(t, resp)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
125
api4/config.go
Обычный файл
125
api4/config.go
Обычный файл
@@ -0,0 +1,125 @@
|
|||||||
|
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
package api4
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/mattermost/mattermost-server/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (api *API) InitConfig() {
|
||||||
|
api.BaseRoutes.ApiRoot.Handle("/config", api.ApiSessionRequired(getConfig)).Methods("GET")
|
||||||
|
api.BaseRoutes.ApiRoot.Handle("/config", api.ApiSessionRequired(updateConfig)).Methods("PUT")
|
||||||
|
api.BaseRoutes.ApiRoot.Handle("/config/reload", api.ApiSessionRequired(configReload)).Methods("POST")
|
||||||
|
api.BaseRoutes.ApiRoot.Handle("/config/client", api.ApiHandler(getClientConfig)).Methods("GET")
|
||||||
|
api.BaseRoutes.ApiRoot.Handle("/config/environment", api.ApiSessionRequired(getEnvironmentConfig)).Methods("GET")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getConfig(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
|
if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
||||||
|
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := c.App.GetSanitizedConfig()
|
||||||
|
|
||||||
|
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
|
||||||
|
w.Write([]byte(cfg.ToJson()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func configReload(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
|
if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
||||||
|
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if *c.App.Config().ExperimentalSettings.RestrictSystemAdmin {
|
||||||
|
c.Err = model.NewAppError("configReload", "api.restricted_system_admin", nil, "", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.App.ReloadConfig()
|
||||||
|
|
||||||
|
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
|
||||||
|
ReturnStatusOK(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateConfig(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
|
cfg := model.ConfigFromJson(r.Body)
|
||||||
|
if cfg == nil {
|
||||||
|
c.SetInvalidParam("config")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
||||||
|
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not allow plugin uploads to be toggled through the API
|
||||||
|
cfg.PluginSettings.EnableUploads = c.App.Config().PluginSettings.EnableUploads
|
||||||
|
|
||||||
|
// If the Message Export feature has been toggled in the System Console, rewrite the ExportFromTimestamp field to an
|
||||||
|
// appropriate value. The rewriting occurs here to ensure it doesn't affect values written to the config file
|
||||||
|
// directly and not through the System Console UI.
|
||||||
|
if *cfg.MessageExportSettings.EnableExport != *c.App.Config().MessageExportSettings.EnableExport {
|
||||||
|
if *cfg.MessageExportSettings.EnableExport && *cfg.MessageExportSettings.ExportFromTimestamp == int64(0) {
|
||||||
|
// When the feature is toggled on, use the current timestamp as the start time for future exports.
|
||||||
|
cfg.MessageExportSettings.ExportFromTimestamp = model.NewInt64(model.GetMillis())
|
||||||
|
} else if !*cfg.MessageExportSettings.EnableExport {
|
||||||
|
// When the feature is disabled, reset the timestamp so that the timestamp will be set if
|
||||||
|
// the feature is re-enabled from the System Console in future.
|
||||||
|
cfg.MessageExportSettings.ExportFromTimestamp = model.NewInt64(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err := c.App.SaveConfig(cfg, true)
|
||||||
|
if err != nil {
|
||||||
|
c.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.LogAudit("updateConfig")
|
||||||
|
|
||||||
|
cfg = c.App.GetSanitizedConfig()
|
||||||
|
|
||||||
|
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
|
||||||
|
w.Write([]byte(cfg.ToJson()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func getClientConfig(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
|
format := r.URL.Query().Get("format")
|
||||||
|
|
||||||
|
if format == "" {
|
||||||
|
c.Err = model.NewAppError("getClientConfig", "api.config.client.old_format.app_error", nil, "", http.StatusNotImplemented)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if format != "old" {
|
||||||
|
c.SetInvalidParam("format")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var config map[string]string
|
||||||
|
if len(c.App.Session.UserId) == 0 {
|
||||||
|
config = c.App.LimitedClientConfigWithComputed()
|
||||||
|
} else {
|
||||||
|
config = c.App.ClientConfigWithComputed()
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Write([]byte(model.MapToJson(config)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func getEnvironmentConfig(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
|
if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
||||||
|
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
envConfig := c.App.GetEnvironmentConfig()
|
||||||
|
|
||||||
|
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
|
||||||
|
w.Write([]byte(model.StringInterfaceToJson(envConfig)))
|
||||||
|
}
|
||||||
326
api4/config_test.go
Обычный файл
326
api4/config_test.go
Обычный файл
@@ -0,0 +1,326 @@
|
|||||||
|
package api4
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/mattermost/mattermost-server/model"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetConfig(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
Client := th.Client
|
||||||
|
|
||||||
|
_, resp := Client.GetConfig()
|
||||||
|
CheckForbiddenStatus(t, resp)
|
||||||
|
|
||||||
|
cfg, resp := th.SystemAdminClient.GetConfig()
|
||||||
|
CheckNoError(t, resp)
|
||||||
|
|
||||||
|
require.NotEqual(t, "", cfg.TeamSettings.SiteName)
|
||||||
|
|
||||||
|
if *cfg.LdapSettings.BindPassword != model.FAKE_SETTING && len(*cfg.LdapSettings.BindPassword) != 0 {
|
||||||
|
t.Fatal("did not sanitize properly")
|
||||||
|
}
|
||||||
|
if *cfg.FileSettings.PublicLinkSalt != model.FAKE_SETTING {
|
||||||
|
t.Fatal("did not sanitize properly")
|
||||||
|
}
|
||||||
|
if *cfg.FileSettings.AmazonS3SecretAccessKey != model.FAKE_SETTING && len(*cfg.FileSettings.AmazonS3SecretAccessKey) != 0 {
|
||||||
|
t.Fatal("did not sanitize properly")
|
||||||
|
}
|
||||||
|
if *cfg.EmailSettings.InviteSalt != model.FAKE_SETTING {
|
||||||
|
t.Fatal("did not sanitize properly")
|
||||||
|
}
|
||||||
|
if *cfg.EmailSettings.SMTPPassword != model.FAKE_SETTING && len(*cfg.EmailSettings.SMTPPassword) != 0 {
|
||||||
|
t.Fatal("did not sanitize properly")
|
||||||
|
}
|
||||||
|
if *cfg.GitLabSettings.Secret != model.FAKE_SETTING && len(*cfg.GitLabSettings.Secret) != 0 {
|
||||||
|
t.Fatal("did not sanitize properly")
|
||||||
|
}
|
||||||
|
if *cfg.SqlSettings.DataSource != model.FAKE_SETTING {
|
||||||
|
t.Fatal("did not sanitize properly")
|
||||||
|
}
|
||||||
|
if *cfg.SqlSettings.AtRestEncryptKey != model.FAKE_SETTING {
|
||||||
|
t.Fatal("did not sanitize properly")
|
||||||
|
}
|
||||||
|
if !strings.Contains(strings.Join(cfg.SqlSettings.DataSourceReplicas, " "), model.FAKE_SETTING) && len(cfg.SqlSettings.DataSourceReplicas) != 0 {
|
||||||
|
t.Fatal("did not sanitize properly")
|
||||||
|
}
|
||||||
|
if !strings.Contains(strings.Join(cfg.SqlSettings.DataSourceSearchReplicas, " "), model.FAKE_SETTING) && len(cfg.SqlSettings.DataSourceSearchReplicas) != 0 {
|
||||||
|
t.Fatal("did not sanitize properly")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReloadConfig(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
Client := th.Client
|
||||||
|
|
||||||
|
t.Run("as system user", func(t *testing.T) {
|
||||||
|
ok, resp := Client.ReloadConfig()
|
||||||
|
CheckForbiddenStatus(t, resp)
|
||||||
|
if ok {
|
||||||
|
t.Fatal("should not Reload the config due no permission.")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("as system admin", func(t *testing.T) {
|
||||||
|
ok, resp := th.SystemAdminClient.ReloadConfig()
|
||||||
|
CheckNoError(t, resp)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("should Reload the config")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("as restricted system admin", func(t *testing.T) {
|
||||||
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ExperimentalSettings.RestrictSystemAdmin = true })
|
||||||
|
|
||||||
|
ok, resp := Client.ReloadConfig()
|
||||||
|
CheckForbiddenStatus(t, resp)
|
||||||
|
if ok {
|
||||||
|
t.Fatal("should not Reload the config due no permission.")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateConfig(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
Client := th.Client
|
||||||
|
|
||||||
|
cfg, resp := th.SystemAdminClient.GetConfig()
|
||||||
|
CheckNoError(t, resp)
|
||||||
|
|
||||||
|
_, resp = Client.UpdateConfig(cfg)
|
||||||
|
CheckForbiddenStatus(t, resp)
|
||||||
|
|
||||||
|
SiteName := th.App.Config().TeamSettings.SiteName
|
||||||
|
|
||||||
|
*cfg.TeamSettings.SiteName = "MyFancyName"
|
||||||
|
cfg, resp = th.SystemAdminClient.UpdateConfig(cfg)
|
||||||
|
CheckNoError(t, resp)
|
||||||
|
|
||||||
|
require.Equal(t, "MyFancyName", *cfg.TeamSettings.SiteName, "It should update the SiteName")
|
||||||
|
|
||||||
|
//Revert the change
|
||||||
|
cfg.TeamSettings.SiteName = SiteName
|
||||||
|
cfg, resp = th.SystemAdminClient.UpdateConfig(cfg)
|
||||||
|
CheckNoError(t, resp)
|
||||||
|
|
||||||
|
require.Equal(t, SiteName, cfg.TeamSettings.SiteName, "It should update the SiteName")
|
||||||
|
|
||||||
|
t.Run("Should not be able to modify PluginSettings.EnableUploads", func(t *testing.T) {
|
||||||
|
oldEnableUploads := *th.App.Config().PluginSettings.EnableUploads
|
||||||
|
*cfg.PluginSettings.EnableUploads = !oldEnableUploads
|
||||||
|
|
||||||
|
cfg, resp = th.SystemAdminClient.UpdateConfig(cfg)
|
||||||
|
CheckNoError(t, resp)
|
||||||
|
assert.Equal(t, oldEnableUploads, *cfg.PluginSettings.EnableUploads)
|
||||||
|
assert.Equal(t, oldEnableUploads, *th.App.Config().PluginSettings.EnableUploads)
|
||||||
|
|
||||||
|
cfg.PluginSettings.EnableUploads = nil
|
||||||
|
cfg, resp = th.SystemAdminClient.UpdateConfig(cfg)
|
||||||
|
CheckNoError(t, resp)
|
||||||
|
assert.Equal(t, oldEnableUploads, *cfg.PluginSettings.EnableUploads)
|
||||||
|
assert.Equal(t, oldEnableUploads, *th.App.Config().PluginSettings.EnableUploads)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateConfigMessageExportSpecialHandling(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
messageExportEnabled := *th.App.Config().MessageExportSettings.EnableExport
|
||||||
|
messageExportTimestamp := *th.App.Config().MessageExportSettings.ExportFromTimestamp
|
||||||
|
|
||||||
|
defer th.App.UpdateConfig(func(cfg *model.Config) {
|
||||||
|
*cfg.MessageExportSettings.EnableExport = messageExportEnabled
|
||||||
|
*cfg.MessageExportSettings.ExportFromTimestamp = messageExportTimestamp
|
||||||
|
})
|
||||||
|
|
||||||
|
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||||
|
*cfg.MessageExportSettings.EnableExport = false
|
||||||
|
*cfg.MessageExportSettings.ExportFromTimestamp = int64(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Turn it on, timestamp should be updated.
|
||||||
|
cfg, resp := th.SystemAdminClient.GetConfig()
|
||||||
|
CheckNoError(t, resp)
|
||||||
|
|
||||||
|
*cfg.MessageExportSettings.EnableExport = true
|
||||||
|
cfg, resp = th.SystemAdminClient.UpdateConfig(cfg)
|
||||||
|
CheckNoError(t, resp)
|
||||||
|
|
||||||
|
assert.True(t, *th.App.Config().MessageExportSettings.EnableExport)
|
||||||
|
assert.NotEqual(t, int64(0), *th.App.Config().MessageExportSettings.ExportFromTimestamp)
|
||||||
|
|
||||||
|
// Turn it off, timestamp should be cleared.
|
||||||
|
cfg, resp = th.SystemAdminClient.GetConfig()
|
||||||
|
CheckNoError(t, resp)
|
||||||
|
|
||||||
|
*cfg.MessageExportSettings.EnableExport = false
|
||||||
|
cfg, resp = th.SystemAdminClient.UpdateConfig(cfg)
|
||||||
|
CheckNoError(t, resp)
|
||||||
|
|
||||||
|
assert.False(t, *th.App.Config().MessageExportSettings.EnableExport)
|
||||||
|
assert.Equal(t, int64(0), *th.App.Config().MessageExportSettings.ExportFromTimestamp)
|
||||||
|
|
||||||
|
// Set a value from the config file.
|
||||||
|
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||||
|
*cfg.MessageExportSettings.EnableExport = false
|
||||||
|
*cfg.MessageExportSettings.ExportFromTimestamp = int64(12345)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Turn it on, timestamp should *not* be updated.
|
||||||
|
cfg, resp = th.SystemAdminClient.GetConfig()
|
||||||
|
CheckNoError(t, resp)
|
||||||
|
|
||||||
|
*cfg.MessageExportSettings.EnableExport = true
|
||||||
|
cfg, resp = th.SystemAdminClient.UpdateConfig(cfg)
|
||||||
|
CheckNoError(t, resp)
|
||||||
|
|
||||||
|
assert.True(t, *th.App.Config().MessageExportSettings.EnableExport)
|
||||||
|
assert.Equal(t, int64(12345), *th.App.Config().MessageExportSettings.ExportFromTimestamp)
|
||||||
|
|
||||||
|
// Turn it off, timestamp should be cleared.
|
||||||
|
cfg, resp = th.SystemAdminClient.GetConfig()
|
||||||
|
CheckNoError(t, resp)
|
||||||
|
|
||||||
|
*cfg.MessageExportSettings.EnableExport = false
|
||||||
|
cfg, resp = th.SystemAdminClient.UpdateConfig(cfg)
|
||||||
|
CheckNoError(t, resp)
|
||||||
|
|
||||||
|
assert.False(t, *th.App.Config().MessageExportSettings.EnableExport)
|
||||||
|
assert.Equal(t, int64(0), *th.App.Config().MessageExportSettings.ExportFromTimestamp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetEnvironmentConfig(t *testing.T) {
|
||||||
|
os.Setenv("MM_SERVICESETTINGS_SITEURL", "http://example.mattermost.com")
|
||||||
|
os.Setenv("MM_SERVICESETTINGS_ENABLECUSTOMEMOJI", "true")
|
||||||
|
defer os.Unsetenv("MM_SERVICESETTINGS_SITEURL")
|
||||||
|
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
t.Run("as system admin", func(t *testing.T) {
|
||||||
|
SystemAdminClient := th.SystemAdminClient
|
||||||
|
|
||||||
|
envConfig, resp := SystemAdminClient.GetEnvironmentConfig()
|
||||||
|
CheckNoError(t, resp)
|
||||||
|
|
||||||
|
if serviceSettings, ok := envConfig["ServiceSettings"]; !ok {
|
||||||
|
t.Fatal("should've returned ServiceSettings")
|
||||||
|
} else if serviceSettingsAsMap, ok := serviceSettings.(map[string]interface{}); !ok {
|
||||||
|
t.Fatal("should've returned ServiceSettings as a map")
|
||||||
|
} else {
|
||||||
|
if siteURL, ok := serviceSettingsAsMap["SiteURL"]; !ok {
|
||||||
|
t.Fatal("should've returned ServiceSettings.SiteURL")
|
||||||
|
} else if siteURLAsBool, ok := siteURL.(bool); !ok {
|
||||||
|
t.Fatal("should've returned ServiceSettings.SiteURL as a boolean")
|
||||||
|
} else if !siteURLAsBool {
|
||||||
|
t.Fatal("should've returned ServiceSettings.SiteURL as true")
|
||||||
|
}
|
||||||
|
|
||||||
|
if enableCustomEmoji, ok := serviceSettingsAsMap["EnableCustomEmoji"]; !ok {
|
||||||
|
t.Fatal("should've returned ServiceSettings.EnableCustomEmoji")
|
||||||
|
} else if enableCustomEmojiAsBool, ok := enableCustomEmoji.(bool); !ok {
|
||||||
|
t.Fatal("should've returned ServiceSettings.EnableCustomEmoji as a boolean")
|
||||||
|
} else if !enableCustomEmojiAsBool {
|
||||||
|
t.Fatal("should've returned ServiceSettings.EnableCustomEmoji as true")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := envConfig["TeamSettings"]; ok {
|
||||||
|
t.Fatal("should not have returned TeamSettings")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("as team admin", func(t *testing.T) {
|
||||||
|
TeamAdminClient := th.CreateClient()
|
||||||
|
th.LoginTeamAdminWithClient(TeamAdminClient)
|
||||||
|
|
||||||
|
_, resp := TeamAdminClient.GetEnvironmentConfig()
|
||||||
|
CheckForbiddenStatus(t, resp)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("as regular user", func(t *testing.T) {
|
||||||
|
Client := th.Client
|
||||||
|
|
||||||
|
_, resp := Client.GetEnvironmentConfig()
|
||||||
|
CheckForbiddenStatus(t, resp)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("as not-regular user", func(t *testing.T) {
|
||||||
|
Client := th.CreateClient()
|
||||||
|
|
||||||
|
_, resp := Client.GetEnvironmentConfig()
|
||||||
|
CheckUnauthorizedStatus(t, resp)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetOldClientConfig(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
testKey := "supersecretkey"
|
||||||
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.GoogleDeveloperKey = testKey })
|
||||||
|
|
||||||
|
t.Run("with session", func(t *testing.T) {
|
||||||
|
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||||
|
*cfg.ServiceSettings.GoogleDeveloperKey = testKey
|
||||||
|
})
|
||||||
|
|
||||||
|
Client := th.Client
|
||||||
|
|
||||||
|
config, resp := Client.GetOldClientConfig("")
|
||||||
|
CheckNoError(t, resp)
|
||||||
|
|
||||||
|
if len(config["Version"]) == 0 {
|
||||||
|
t.Fatal("config not returned correctly")
|
||||||
|
}
|
||||||
|
|
||||||
|
if config["GoogleDeveloperKey"] != testKey {
|
||||||
|
t.Fatal("config missing developer key")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("without session", func(t *testing.T) {
|
||||||
|
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||||
|
*cfg.ServiceSettings.GoogleDeveloperKey = testKey
|
||||||
|
})
|
||||||
|
|
||||||
|
Client := th.CreateClient()
|
||||||
|
|
||||||
|
config, resp := Client.GetOldClientConfig("")
|
||||||
|
CheckNoError(t, resp)
|
||||||
|
|
||||||
|
if len(config["Version"]) == 0 {
|
||||||
|
t.Fatal("config not returned correctly")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := config["GoogleDeveloperKey"]; ok {
|
||||||
|
t.Fatal("config should be missing developer key")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("missing format", func(t *testing.T) {
|
||||||
|
Client := th.Client
|
||||||
|
|
||||||
|
if _, err := Client.DoApiGet("/config/client", ""); err == nil || err.StatusCode != http.StatusNotImplemented {
|
||||||
|
t.Fatal("should have errored with 501")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("invalid format", func(t *testing.T) {
|
||||||
|
Client := th.Client
|
||||||
|
|
||||||
|
if _, err := Client.DoApiGet("/config/client?format=junk", ""); err == nil || err.StatusCode != http.StatusBadRequest {
|
||||||
|
t.Fatal("should have errored with 400")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -25,6 +25,11 @@ func testElasticsearch(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if *c.App.Config().ExperimentalSettings.RestrictSystemAdmin {
|
||||||
|
c.Err = model.NewAppError("testElasticsearch", "api.restricted_system_admin", nil, "", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if err := c.App.TestElasticsearch(cfg); err != nil {
|
if err := c.App.TestElasticsearch(cfg); err != nil {
|
||||||
c.Err = err
|
c.Err = err
|
||||||
return
|
return
|
||||||
@@ -39,6 +44,11 @@ func purgeElasticsearchIndexes(c *Context, w http.ResponseWriter, r *http.Reques
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if *c.App.Config().ExperimentalSettings.RestrictSystemAdmin {
|
||||||
|
c.Err = model.NewAppError("purgeElasticsearchIndexes", "api.restricted_system_admin", nil, "", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if err := c.App.PurgeElasticsearchIndexes(); err != nil {
|
if err := c.App.PurgeElasticsearchIndexes(); err != nil {
|
||||||
c.Err = err
|
c.Err = err
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -5,26 +5,50 @@ package api4
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/mattermost/mattermost-server/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestElasticsearchTest(t *testing.T) {
|
func TestElasticsearchTest(t *testing.T) {
|
||||||
th := Setup().InitBasic()
|
th := Setup().InitBasic()
|
||||||
defer th.TearDown()
|
defer th.TearDown()
|
||||||
|
|
||||||
_, resp := th.Client.TestElasticsearch()
|
t.Run("as system user", func(t *testing.T) {
|
||||||
CheckForbiddenStatus(t, resp)
|
_, resp := th.Client.TestElasticsearch()
|
||||||
|
CheckForbiddenStatus(t, resp)
|
||||||
|
})
|
||||||
|
|
||||||
_, resp = th.SystemAdminClient.TestElasticsearch()
|
t.Run("as system admin", func(t *testing.T) {
|
||||||
CheckNotImplementedStatus(t, resp)
|
_, resp := th.SystemAdminClient.TestElasticsearch()
|
||||||
|
CheckNotImplementedStatus(t, resp)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("as restricted system admin", func(t *testing.T) {
|
||||||
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ExperimentalSettings.RestrictSystemAdmin = true })
|
||||||
|
|
||||||
|
_, resp := th.SystemAdminClient.TestElasticsearch()
|
||||||
|
CheckForbiddenStatus(t, resp)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestElasticsearchPurgeIndexes(t *testing.T) {
|
func TestElasticsearchPurgeIndexes(t *testing.T) {
|
||||||
th := Setup().InitBasic()
|
th := Setup().InitBasic()
|
||||||
defer th.TearDown()
|
defer th.TearDown()
|
||||||
|
|
||||||
_, resp := th.Client.PurgeElasticsearchIndexes()
|
t.Run("as system user", func(t *testing.T) {
|
||||||
CheckForbiddenStatus(t, resp)
|
_, resp := th.Client.PurgeElasticsearchIndexes()
|
||||||
|
CheckForbiddenStatus(t, resp)
|
||||||
|
})
|
||||||
|
|
||||||
_, resp = th.SystemAdminClient.PurgeElasticsearchIndexes()
|
t.Run("as system admin", func(t *testing.T) {
|
||||||
CheckNotImplementedStatus(t, resp)
|
_, resp := th.SystemAdminClient.PurgeElasticsearchIndexes()
|
||||||
|
CheckNotImplementedStatus(t, resp)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("as restricted system admin", func(t *testing.T) {
|
||||||
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ExperimentalSettings.RestrictSystemAdmin = true })
|
||||||
|
|
||||||
|
_, resp := th.SystemAdminClient.PurgeElasticsearchIndexes()
|
||||||
|
CheckForbiddenStatus(t, resp)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
131
api4/license.go
Обычный файл
131
api4/license.go
Обычный файл
@@ -0,0 +1,131 @@
|
|||||||
|
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
package api4
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/mattermost/mattermost-server/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (api *API) InitLicense() {
|
||||||
|
api.BaseRoutes.ApiRoot.Handle("/license", api.ApiSessionRequired(addLicense)).Methods("POST")
|
||||||
|
api.BaseRoutes.ApiRoot.Handle("/license", api.ApiSessionRequired(removeLicense)).Methods("DELETE")
|
||||||
|
api.BaseRoutes.ApiRoot.Handle("/license/client", api.ApiHandler(getClientLicense)).Methods("GET")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getClientLicense(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
|
format := r.URL.Query().Get("format")
|
||||||
|
|
||||||
|
if format == "" {
|
||||||
|
c.Err = model.NewAppError("getClientLicense", "api.license.client.old_format.app_error", nil, "", http.StatusNotImplemented)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if format != "old" {
|
||||||
|
c.SetInvalidParam("format")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
etag := c.App.GetClientLicenseEtag(true)
|
||||||
|
if c.HandleEtag(etag, "Get Client License", w, r) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var clientLicense map[string]string
|
||||||
|
|
||||||
|
if c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
||||||
|
clientLicense = c.App.ClientLicense()
|
||||||
|
} else {
|
||||||
|
clientLicense = c.App.GetSanitizedClientLicense()
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set(model.HEADER_ETAG_SERVER, etag)
|
||||||
|
w.Write([]byte(model.MapToJson(clientLicense)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func addLicense(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
|
c.LogAudit("attempt")
|
||||||
|
|
||||||
|
if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
||||||
|
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if *c.App.Config().ExperimentalSettings.RestrictSystemAdmin {
|
||||||
|
c.Err = model.NewAppError("addLicense", "api.restricted_system_admin", nil, "", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err := r.ParseMultipartForm(*c.App.Config().FileSettings.MaxFileSize)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
m := r.MultipartForm
|
||||||
|
|
||||||
|
fileArray, ok := m.File["license"]
|
||||||
|
if !ok {
|
||||||
|
c.Err = model.NewAppError("addLicense", "api.license.add_license.no_file.app_error", nil, "", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(fileArray) <= 0 {
|
||||||
|
c.Err = model.NewAppError("addLicense", "api.license.add_license.array.app_error", nil, "", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fileData := fileArray[0]
|
||||||
|
|
||||||
|
file, err := fileData.Open()
|
||||||
|
if err != nil {
|
||||||
|
c.Err = model.NewAppError("addLicense", "api.license.add_license.open.app_error", nil, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
|
io.Copy(buf, file)
|
||||||
|
|
||||||
|
license, appErr := c.App.SaveLicense(buf.Bytes())
|
||||||
|
if appErr != nil {
|
||||||
|
if appErr.Id == model.EXPIRED_LICENSE_ERROR {
|
||||||
|
c.LogAudit("failed - expired or non-started license")
|
||||||
|
} else if appErr.Id == model.INVALID_LICENSE_ERROR {
|
||||||
|
c.LogAudit("failed - invalid license")
|
||||||
|
} else {
|
||||||
|
c.LogAudit("failed - unable to save license")
|
||||||
|
}
|
||||||
|
c.Err = appErr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.LogAudit("success")
|
||||||
|
w.Write([]byte(license.ToJson()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeLicense(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
|
c.LogAudit("attempt")
|
||||||
|
|
||||||
|
if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
||||||
|
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if *c.App.Config().ExperimentalSettings.RestrictSystemAdmin {
|
||||||
|
c.Err = model.NewAppError("removeLicense", "api.restricted_system_admin", nil, "", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.App.RemoveLicense(); err != nil {
|
||||||
|
c.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.LogAudit("success")
|
||||||
|
ReturnStatusOK(w)
|
||||||
|
}
|
||||||
105
api4/license_test.go
Обычный файл
105
api4/license_test.go
Обычный файл
@@ -0,0 +1,105 @@
|
|||||||
|
package api4
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/mattermost/mattermost-server/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetOldClientLicense(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
Client := th.Client
|
||||||
|
|
||||||
|
license, resp := Client.GetOldClientLicense("")
|
||||||
|
CheckNoError(t, resp)
|
||||||
|
|
||||||
|
if len(license["IsLicensed"]) == 0 {
|
||||||
|
t.Fatal("license not returned correctly")
|
||||||
|
}
|
||||||
|
|
||||||
|
Client.Logout()
|
||||||
|
|
||||||
|
_, resp = Client.GetOldClientLicense("")
|
||||||
|
CheckNoError(t, resp)
|
||||||
|
|
||||||
|
if _, err := Client.DoApiGet("/license/client", ""); err == nil || err.StatusCode != http.StatusNotImplemented {
|
||||||
|
t.Fatal("should have errored with 501")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := Client.DoApiGet("/license/client?format=junk", ""); err == nil || err.StatusCode != http.StatusBadRequest {
|
||||||
|
t.Fatal("should have errored with 400")
|
||||||
|
}
|
||||||
|
|
||||||
|
license, resp = th.SystemAdminClient.GetOldClientLicense("")
|
||||||
|
CheckNoError(t, resp)
|
||||||
|
|
||||||
|
if len(license["IsLicensed"]) == 0 {
|
||||||
|
t.Fatal("license not returned correctly")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUploadLicenseFile(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
Client := th.Client
|
||||||
|
|
||||||
|
t.Run("as system user", func(t *testing.T) {
|
||||||
|
ok, resp := Client.UploadLicenseFile([]byte{})
|
||||||
|
CheckForbiddenStatus(t, resp)
|
||||||
|
if ok {
|
||||||
|
t.Fatal("should fail")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("as system admin user", func(t *testing.T) {
|
||||||
|
ok, resp := th.SystemAdminClient.UploadLicenseFile([]byte{})
|
||||||
|
CheckBadRequestStatus(t, resp)
|
||||||
|
if ok {
|
||||||
|
t.Fatal("should fail")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("as restricted system admin user", func(t *testing.T) {
|
||||||
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ExperimentalSettings.RestrictSystemAdmin = true })
|
||||||
|
|
||||||
|
ok, resp := th.SystemAdminClient.UploadLicenseFile([]byte{})
|
||||||
|
CheckForbiddenStatus(t, resp)
|
||||||
|
if ok {
|
||||||
|
t.Fatal("should fail")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemoveLicenseFile(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
Client := th.Client
|
||||||
|
|
||||||
|
t.Run("as system user", func(t *testing.T) {
|
||||||
|
ok, resp := Client.RemoveLicenseFile()
|
||||||
|
CheckForbiddenStatus(t, resp)
|
||||||
|
if ok {
|
||||||
|
t.Fatal("should fail")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("as system admin user", func(t *testing.T) {
|
||||||
|
ok, resp := th.SystemAdminClient.RemoveLicenseFile()
|
||||||
|
CheckNoError(t, resp)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("should pass")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("as restricted system admin user", func(t *testing.T) {
|
||||||
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ExperimentalSettings.RestrictSystemAdmin = true })
|
||||||
|
|
||||||
|
ok, resp := th.SystemAdminClient.RemoveLicenseFile()
|
||||||
|
CheckForbiddenStatus(t, resp)
|
||||||
|
if ok {
|
||||||
|
t.Fatal("should fail")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
238
api4/system.go
238
api4/system.go
@@ -4,10 +4,8 @@
|
|||||||
package api4
|
package api4
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
@@ -26,16 +24,6 @@ func (api *API) InitSystem() {
|
|||||||
|
|
||||||
api.BaseRoutes.System.Handle("/timezones", api.ApiSessionRequired(getSupportedTimezones)).Methods("GET")
|
api.BaseRoutes.System.Handle("/timezones", api.ApiSessionRequired(getSupportedTimezones)).Methods("GET")
|
||||||
|
|
||||||
api.BaseRoutes.ApiRoot.Handle("/config", api.ApiSessionRequired(getConfig)).Methods("GET")
|
|
||||||
api.BaseRoutes.ApiRoot.Handle("/config", api.ApiSessionRequired(updateConfig)).Methods("PUT")
|
|
||||||
api.BaseRoutes.ApiRoot.Handle("/config/reload", api.ApiSessionRequired(configReload)).Methods("POST")
|
|
||||||
api.BaseRoutes.ApiRoot.Handle("/config/client", api.ApiHandler(getClientConfig)).Methods("GET")
|
|
||||||
api.BaseRoutes.ApiRoot.Handle("/config/environment", api.ApiSessionRequired(getEnvironmentConfig)).Methods("GET")
|
|
||||||
|
|
||||||
api.BaseRoutes.ApiRoot.Handle("/license", api.ApiSessionRequired(addLicense)).Methods("POST")
|
|
||||||
api.BaseRoutes.ApiRoot.Handle("/license", api.ApiSessionRequired(removeLicense)).Methods("DELETE")
|
|
||||||
api.BaseRoutes.ApiRoot.Handle("/license/client", api.ApiHandler(getClientLicense)).Methods("GET")
|
|
||||||
|
|
||||||
api.BaseRoutes.ApiRoot.Handle("/audits", api.ApiSessionRequired(getAudits)).Methods("GET")
|
api.BaseRoutes.ApiRoot.Handle("/audits", api.ApiSessionRequired(getAudits)).Methods("GET")
|
||||||
api.BaseRoutes.ApiRoot.Handle("/email/test", api.ApiSessionRequired(testEmail)).Methods("POST")
|
api.BaseRoutes.ApiRoot.Handle("/email/test", api.ApiSessionRequired(testEmail)).Methods("POST")
|
||||||
api.BaseRoutes.ApiRoot.Handle("/file/s3_test", api.ApiSessionRequired(testS3)).Methods("POST")
|
api.BaseRoutes.ApiRoot.Handle("/file/s3_test", api.ApiSessionRequired(testS3)).Methods("POST")
|
||||||
@@ -88,6 +76,11 @@ func testEmail(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if *c.App.Config().ExperimentalSettings.RestrictSystemAdmin {
|
||||||
|
c.Err = model.NewAppError("testEmail", "api.restricted_system_admin", nil, "", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
err := c.App.TestEmail(c.App.Session.UserId, cfg)
|
err := c.App.TestEmail(c.App.Session.UserId, cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Err = err
|
c.Err = err
|
||||||
@@ -97,73 +90,6 @@ func testEmail(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
ReturnStatusOK(w)
|
ReturnStatusOK(w)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getConfig(c *Context, w http.ResponseWriter, r *http.Request) {
|
|
||||||
if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
|
||||||
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg := c.App.GetSanitizedConfig()
|
|
||||||
|
|
||||||
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
|
|
||||||
w.Write([]byte(cfg.ToJson()))
|
|
||||||
}
|
|
||||||
|
|
||||||
func configReload(c *Context, w http.ResponseWriter, r *http.Request) {
|
|
||||||
if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
|
||||||
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.App.ReloadConfig()
|
|
||||||
|
|
||||||
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
|
|
||||||
ReturnStatusOK(w)
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateConfig(c *Context, w http.ResponseWriter, r *http.Request) {
|
|
||||||
cfg := model.ConfigFromJson(r.Body)
|
|
||||||
if cfg == nil {
|
|
||||||
c.SetInvalidParam("config")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
|
||||||
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do not allow plugin uploads to be toggled through the API
|
|
||||||
cfg.PluginSettings.EnableUploads = c.App.Config().PluginSettings.EnableUploads
|
|
||||||
|
|
||||||
// If the Message Export feature has been toggled in the System Console, rewrite the ExportFromTimestamp field to an
|
|
||||||
// appropriate value. The rewriting occurs here to ensure it doesn't affect values written to the config file
|
|
||||||
// directly and not through the System Console UI.
|
|
||||||
if *cfg.MessageExportSettings.EnableExport != *c.App.Config().MessageExportSettings.EnableExport {
|
|
||||||
if *cfg.MessageExportSettings.EnableExport && *cfg.MessageExportSettings.ExportFromTimestamp == int64(0) {
|
|
||||||
// When the feature is toggled on, use the current timestamp as the start time for future exports.
|
|
||||||
cfg.MessageExportSettings.ExportFromTimestamp = model.NewInt64(model.GetMillis())
|
|
||||||
} else if !*cfg.MessageExportSettings.EnableExport {
|
|
||||||
// When the feature is disabled, reset the timestamp so that the timestamp will be set if
|
|
||||||
// the feature is re-enabled from the System Console in future.
|
|
||||||
cfg.MessageExportSettings.ExportFromTimestamp = model.NewInt64(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err := c.App.SaveConfig(cfg, true)
|
|
||||||
if err != nil {
|
|
||||||
c.Err = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.LogAudit("updateConfig")
|
|
||||||
|
|
||||||
cfg = c.App.GetSanitizedConfig()
|
|
||||||
|
|
||||||
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
|
|
||||||
w.Write([]byte(cfg.ToJson()))
|
|
||||||
}
|
|
||||||
|
|
||||||
func getAudits(c *Context, w http.ResponseWriter, r *http.Request) {
|
func getAudits(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
||||||
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||||
@@ -181,12 +107,16 @@ func getAudits(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func databaseRecycle(c *Context, w http.ResponseWriter, r *http.Request) {
|
func databaseRecycle(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
||||||
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if *c.App.Config().ExperimentalSettings.RestrictSystemAdmin {
|
||||||
|
c.Err = model.NewAppError("databaseRecycle", "api.restricted_system_admin", nil, "", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
c.App.RecycleDatabaseConnection()
|
c.App.RecycleDatabaseConnection()
|
||||||
|
|
||||||
ReturnStatusOK(w)
|
ReturnStatusOK(w)
|
||||||
@@ -198,6 +128,11 @@ func invalidateCaches(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if *c.App.Config().ExperimentalSettings.RestrictSystemAdmin {
|
||||||
|
c.Err = model.NewAppError("invalidateCaches", "api.restricted_system_admin", nil, "", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
err := c.App.InvalidateAllCaches()
|
err := c.App.InvalidateAllCaches()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Err = err
|
c.Err = err
|
||||||
@@ -259,144 +194,6 @@ func postLog(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
w.Write([]byte(model.MapToJson(m)))
|
w.Write([]byte(model.MapToJson(m)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func getClientConfig(c *Context, w http.ResponseWriter, r *http.Request) {
|
|
||||||
format := r.URL.Query().Get("format")
|
|
||||||
|
|
||||||
if format == "" {
|
|
||||||
c.Err = model.NewAppError("getClientConfig", "api.config.client.old_format.app_error", nil, "", http.StatusNotImplemented)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if format != "old" {
|
|
||||||
c.SetInvalidParam("format")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var config map[string]string
|
|
||||||
if len(c.App.Session.UserId) == 0 {
|
|
||||||
config = c.App.LimitedClientConfigWithComputed()
|
|
||||||
} else {
|
|
||||||
config = c.App.ClientConfigWithComputed()
|
|
||||||
}
|
|
||||||
|
|
||||||
w.Write([]byte(model.MapToJson(config)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func getEnvironmentConfig(c *Context, w http.ResponseWriter, r *http.Request) {
|
|
||||||
if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
|
||||||
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
envConfig := c.App.GetEnvironmentConfig()
|
|
||||||
|
|
||||||
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
|
|
||||||
w.Write([]byte(model.StringInterfaceToJson(envConfig)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func getClientLicense(c *Context, w http.ResponseWriter, r *http.Request) {
|
|
||||||
format := r.URL.Query().Get("format")
|
|
||||||
|
|
||||||
if format == "" {
|
|
||||||
c.Err = model.NewAppError("getClientLicense", "api.license.client.old_format.app_error", nil, "", http.StatusNotImplemented)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if format != "old" {
|
|
||||||
c.SetInvalidParam("format")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
etag := c.App.GetClientLicenseEtag(true)
|
|
||||||
if c.HandleEtag(etag, "Get Client License", w, r) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var clientLicense map[string]string
|
|
||||||
|
|
||||||
if c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
|
||||||
clientLicense = c.App.ClientLicense()
|
|
||||||
} else {
|
|
||||||
clientLicense = c.App.GetSanitizedClientLicense()
|
|
||||||
}
|
|
||||||
|
|
||||||
w.Header().Set(model.HEADER_ETAG_SERVER, etag)
|
|
||||||
w.Write([]byte(model.MapToJson(clientLicense)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func addLicense(c *Context, w http.ResponseWriter, r *http.Request) {
|
|
||||||
c.LogAudit("attempt")
|
|
||||||
|
|
||||||
if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
|
||||||
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err := r.ParseMultipartForm(*c.App.Config().FileSettings.MaxFileSize)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
m := r.MultipartForm
|
|
||||||
|
|
||||||
fileArray, ok := m.File["license"]
|
|
||||||
if !ok {
|
|
||||||
c.Err = model.NewAppError("addLicense", "api.license.add_license.no_file.app_error", nil, "", http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(fileArray) <= 0 {
|
|
||||||
c.Err = model.NewAppError("addLicense", "api.license.add_license.array.app_error", nil, "", http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
fileData := fileArray[0]
|
|
||||||
|
|
||||||
file, err := fileData.Open()
|
|
||||||
if err != nil {
|
|
||||||
c.Err = model.NewAppError("addLicense", "api.license.add_license.open.app_error", nil, err.Error(), http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
buf := bytes.NewBuffer(nil)
|
|
||||||
io.Copy(buf, file)
|
|
||||||
|
|
||||||
license, appErr := c.App.SaveLicense(buf.Bytes())
|
|
||||||
if appErr != nil {
|
|
||||||
if appErr.Id == model.EXPIRED_LICENSE_ERROR {
|
|
||||||
c.LogAudit("failed - expired or non-started license")
|
|
||||||
} else if appErr.Id == model.INVALID_LICENSE_ERROR {
|
|
||||||
c.LogAudit("failed - invalid license")
|
|
||||||
} else {
|
|
||||||
c.LogAudit("failed - unable to save license")
|
|
||||||
}
|
|
||||||
c.Err = appErr
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.LogAudit("success")
|
|
||||||
w.Write([]byte(license.ToJson()))
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeLicense(c *Context, w http.ResponseWriter, r *http.Request) {
|
|
||||||
c.LogAudit("attempt")
|
|
||||||
|
|
||||||
if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
|
||||||
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := c.App.RemoveLicense(); err != nil {
|
|
||||||
c.Err = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.LogAudit("success")
|
|
||||||
ReturnStatusOK(w)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getAnalytics(c *Context, w http.ResponseWriter, r *http.Request) {
|
func getAnalytics(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
name := r.URL.Query().Get("name")
|
name := r.URL.Query().Get("name")
|
||||||
teamId := r.URL.Query().Get("team_id")
|
teamId := r.URL.Query().Get("team_id")
|
||||||
@@ -450,6 +247,11 @@ func testS3(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if *c.App.Config().ExperimentalSettings.RestrictSystemAdmin {
|
||||||
|
c.Err = model.NewAppError("testS3", "api.restricted_system_admin", nil, "", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
err := filesstore.CheckMandatoryS3Fields(&cfg.FileSettings)
|
err := filesstore.CheckMandatoryS3Fields(&cfg.FileSettings)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Err = err
|
c.Err = err
|
||||||
|
|||||||
@@ -5,13 +5,11 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost-server/mlog"
|
"github.com/mattermost/mattermost-server/mlog"
|
||||||
"github.com/mattermost/mattermost-server/model"
|
"github.com/mattermost/mattermost-server/model"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetPing(t *testing.T) {
|
func TestGetPing(t *testing.T) {
|
||||||
@@ -38,342 +36,6 @@ func TestGetPing(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetConfig(t *testing.T) {
|
|
||||||
th := Setup().InitBasic()
|
|
||||||
defer th.TearDown()
|
|
||||||
Client := th.Client
|
|
||||||
|
|
||||||
_, resp := Client.GetConfig()
|
|
||||||
CheckForbiddenStatus(t, resp)
|
|
||||||
|
|
||||||
cfg, resp := th.SystemAdminClient.GetConfig()
|
|
||||||
CheckNoError(t, resp)
|
|
||||||
|
|
||||||
require.NotEqual(t, "", cfg.TeamSettings.SiteName)
|
|
||||||
|
|
||||||
if *cfg.LdapSettings.BindPassword != model.FAKE_SETTING && len(*cfg.LdapSettings.BindPassword) != 0 {
|
|
||||||
t.Fatal("did not sanitize properly")
|
|
||||||
}
|
|
||||||
if *cfg.FileSettings.PublicLinkSalt != model.FAKE_SETTING {
|
|
||||||
t.Fatal("did not sanitize properly")
|
|
||||||
}
|
|
||||||
if *cfg.FileSettings.AmazonS3SecretAccessKey != model.FAKE_SETTING && len(*cfg.FileSettings.AmazonS3SecretAccessKey) != 0 {
|
|
||||||
t.Fatal("did not sanitize properly")
|
|
||||||
}
|
|
||||||
if *cfg.EmailSettings.InviteSalt != model.FAKE_SETTING {
|
|
||||||
t.Fatal("did not sanitize properly")
|
|
||||||
}
|
|
||||||
if *cfg.EmailSettings.SMTPPassword != model.FAKE_SETTING && len(*cfg.EmailSettings.SMTPPassword) != 0 {
|
|
||||||
t.Fatal("did not sanitize properly")
|
|
||||||
}
|
|
||||||
if *cfg.GitLabSettings.Secret != model.FAKE_SETTING && len(*cfg.GitLabSettings.Secret) != 0 {
|
|
||||||
t.Fatal("did not sanitize properly")
|
|
||||||
}
|
|
||||||
if *cfg.SqlSettings.DataSource != model.FAKE_SETTING {
|
|
||||||
t.Fatal("did not sanitize properly")
|
|
||||||
}
|
|
||||||
if *cfg.SqlSettings.AtRestEncryptKey != model.FAKE_SETTING {
|
|
||||||
t.Fatal("did not sanitize properly")
|
|
||||||
}
|
|
||||||
if !strings.Contains(strings.Join(cfg.SqlSettings.DataSourceReplicas, " "), model.FAKE_SETTING) && len(cfg.SqlSettings.DataSourceReplicas) != 0 {
|
|
||||||
t.Fatal("did not sanitize properly")
|
|
||||||
}
|
|
||||||
if !strings.Contains(strings.Join(cfg.SqlSettings.DataSourceSearchReplicas, " "), model.FAKE_SETTING) && len(cfg.SqlSettings.DataSourceSearchReplicas) != 0 {
|
|
||||||
t.Fatal("did not sanitize properly")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReloadConfig(t *testing.T) {
|
|
||||||
th := Setup().InitBasic()
|
|
||||||
defer th.TearDown()
|
|
||||||
Client := th.Client
|
|
||||||
|
|
||||||
flag, resp := Client.ReloadConfig()
|
|
||||||
CheckForbiddenStatus(t, resp)
|
|
||||||
if flag {
|
|
||||||
t.Fatal("should not Reload the config due no permission.")
|
|
||||||
}
|
|
||||||
|
|
||||||
flag, resp = th.SystemAdminClient.ReloadConfig()
|
|
||||||
CheckNoError(t, resp)
|
|
||||||
if !flag {
|
|
||||||
t.Fatal("should Reload the config")
|
|
||||||
}
|
|
||||||
|
|
||||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.MaxUsersPerTeam = 50 })
|
|
||||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.EnableOpenServer = true })
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUpdateConfig(t *testing.T) {
|
|
||||||
th := Setup().InitBasic()
|
|
||||||
defer th.TearDown()
|
|
||||||
Client := th.Client
|
|
||||||
|
|
||||||
cfg, resp := th.SystemAdminClient.GetConfig()
|
|
||||||
CheckNoError(t, resp)
|
|
||||||
|
|
||||||
_, resp = Client.UpdateConfig(cfg)
|
|
||||||
CheckForbiddenStatus(t, resp)
|
|
||||||
|
|
||||||
SiteName := th.App.Config().TeamSettings.SiteName
|
|
||||||
|
|
||||||
*cfg.TeamSettings.SiteName = "MyFancyName"
|
|
||||||
cfg, resp = th.SystemAdminClient.UpdateConfig(cfg)
|
|
||||||
CheckNoError(t, resp)
|
|
||||||
|
|
||||||
require.Equal(t, "MyFancyName", *cfg.TeamSettings.SiteName, "It should update the SiteName")
|
|
||||||
|
|
||||||
//Revert the change
|
|
||||||
cfg.TeamSettings.SiteName = SiteName
|
|
||||||
cfg, resp = th.SystemAdminClient.UpdateConfig(cfg)
|
|
||||||
CheckNoError(t, resp)
|
|
||||||
|
|
||||||
require.Equal(t, SiteName, cfg.TeamSettings.SiteName, "It should update the SiteName")
|
|
||||||
|
|
||||||
t.Run("Should not be able to modify PluginSettings.EnableUploads", func(t *testing.T) {
|
|
||||||
oldEnableUploads := *th.App.Config().PluginSettings.EnableUploads
|
|
||||||
*cfg.PluginSettings.EnableUploads = !oldEnableUploads
|
|
||||||
|
|
||||||
cfg, resp = th.SystemAdminClient.UpdateConfig(cfg)
|
|
||||||
CheckNoError(t, resp)
|
|
||||||
assert.Equal(t, oldEnableUploads, *cfg.PluginSettings.EnableUploads)
|
|
||||||
assert.Equal(t, oldEnableUploads, *th.App.Config().PluginSettings.EnableUploads)
|
|
||||||
|
|
||||||
cfg.PluginSettings.EnableUploads = nil
|
|
||||||
cfg, resp = th.SystemAdminClient.UpdateConfig(cfg)
|
|
||||||
CheckNoError(t, resp)
|
|
||||||
assert.Equal(t, oldEnableUploads, *cfg.PluginSettings.EnableUploads)
|
|
||||||
assert.Equal(t, oldEnableUploads, *th.App.Config().PluginSettings.EnableUploads)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUpdateConfigMessageExportSpecialHandling(t *testing.T) {
|
|
||||||
th := Setup().InitBasic()
|
|
||||||
defer th.TearDown()
|
|
||||||
|
|
||||||
messageExportEnabled := *th.App.Config().MessageExportSettings.EnableExport
|
|
||||||
messageExportTimestamp := *th.App.Config().MessageExportSettings.ExportFromTimestamp
|
|
||||||
|
|
||||||
defer th.App.UpdateConfig(func(cfg *model.Config) {
|
|
||||||
*cfg.MessageExportSettings.EnableExport = messageExportEnabled
|
|
||||||
*cfg.MessageExportSettings.ExportFromTimestamp = messageExportTimestamp
|
|
||||||
})
|
|
||||||
|
|
||||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
|
||||||
*cfg.MessageExportSettings.EnableExport = false
|
|
||||||
*cfg.MessageExportSettings.ExportFromTimestamp = int64(0)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Turn it on, timestamp should be updated.
|
|
||||||
cfg, resp := th.SystemAdminClient.GetConfig()
|
|
||||||
CheckNoError(t, resp)
|
|
||||||
|
|
||||||
*cfg.MessageExportSettings.EnableExport = true
|
|
||||||
cfg, resp = th.SystemAdminClient.UpdateConfig(cfg)
|
|
||||||
CheckNoError(t, resp)
|
|
||||||
|
|
||||||
assert.True(t, *th.App.Config().MessageExportSettings.EnableExport)
|
|
||||||
assert.NotEqual(t, int64(0), *th.App.Config().MessageExportSettings.ExportFromTimestamp)
|
|
||||||
|
|
||||||
// Turn it off, timestamp should be cleared.
|
|
||||||
cfg, resp = th.SystemAdminClient.GetConfig()
|
|
||||||
CheckNoError(t, resp)
|
|
||||||
|
|
||||||
*cfg.MessageExportSettings.EnableExport = false
|
|
||||||
cfg, resp = th.SystemAdminClient.UpdateConfig(cfg)
|
|
||||||
CheckNoError(t, resp)
|
|
||||||
|
|
||||||
assert.False(t, *th.App.Config().MessageExportSettings.EnableExport)
|
|
||||||
assert.Equal(t, int64(0), *th.App.Config().MessageExportSettings.ExportFromTimestamp)
|
|
||||||
|
|
||||||
// Set a value from the config file.
|
|
||||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
|
||||||
*cfg.MessageExportSettings.EnableExport = false
|
|
||||||
*cfg.MessageExportSettings.ExportFromTimestamp = int64(12345)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Turn it on, timestamp should *not* be updated.
|
|
||||||
cfg, resp = th.SystemAdminClient.GetConfig()
|
|
||||||
CheckNoError(t, resp)
|
|
||||||
|
|
||||||
*cfg.MessageExportSettings.EnableExport = true
|
|
||||||
cfg, resp = th.SystemAdminClient.UpdateConfig(cfg)
|
|
||||||
CheckNoError(t, resp)
|
|
||||||
|
|
||||||
assert.True(t, *th.App.Config().MessageExportSettings.EnableExport)
|
|
||||||
assert.Equal(t, int64(12345), *th.App.Config().MessageExportSettings.ExportFromTimestamp)
|
|
||||||
|
|
||||||
// Turn it off, timestamp should be cleared.
|
|
||||||
cfg, resp = th.SystemAdminClient.GetConfig()
|
|
||||||
CheckNoError(t, resp)
|
|
||||||
|
|
||||||
*cfg.MessageExportSettings.EnableExport = false
|
|
||||||
cfg, resp = th.SystemAdminClient.UpdateConfig(cfg)
|
|
||||||
CheckNoError(t, resp)
|
|
||||||
|
|
||||||
assert.False(t, *th.App.Config().MessageExportSettings.EnableExport)
|
|
||||||
assert.Equal(t, int64(0), *th.App.Config().MessageExportSettings.ExportFromTimestamp)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetEnvironmentConfig(t *testing.T) {
|
|
||||||
os.Setenv("MM_SERVICESETTINGS_SITEURL", "http://example.mattermost.com")
|
|
||||||
os.Setenv("MM_SERVICESETTINGS_ENABLECUSTOMEMOJI", "true")
|
|
||||||
defer os.Unsetenv("MM_SERVICESETTINGS_SITEURL")
|
|
||||||
|
|
||||||
th := Setup().InitBasic()
|
|
||||||
defer th.TearDown()
|
|
||||||
|
|
||||||
t.Run("as system admin", func(t *testing.T) {
|
|
||||||
SystemAdminClient := th.SystemAdminClient
|
|
||||||
|
|
||||||
envConfig, resp := SystemAdminClient.GetEnvironmentConfig()
|
|
||||||
CheckNoError(t, resp)
|
|
||||||
|
|
||||||
if serviceSettings, ok := envConfig["ServiceSettings"]; !ok {
|
|
||||||
t.Fatal("should've returned ServiceSettings")
|
|
||||||
} else if serviceSettingsAsMap, ok := serviceSettings.(map[string]interface{}); !ok {
|
|
||||||
t.Fatal("should've returned ServiceSettings as a map")
|
|
||||||
} else {
|
|
||||||
if siteURL, ok := serviceSettingsAsMap["SiteURL"]; !ok {
|
|
||||||
t.Fatal("should've returned ServiceSettings.SiteURL")
|
|
||||||
} else if siteURLAsBool, ok := siteURL.(bool); !ok {
|
|
||||||
t.Fatal("should've returned ServiceSettings.SiteURL as a boolean")
|
|
||||||
} else if !siteURLAsBool {
|
|
||||||
t.Fatal("should've returned ServiceSettings.SiteURL as true")
|
|
||||||
}
|
|
||||||
|
|
||||||
if enableCustomEmoji, ok := serviceSettingsAsMap["EnableCustomEmoji"]; !ok {
|
|
||||||
t.Fatal("should've returned ServiceSettings.EnableCustomEmoji")
|
|
||||||
} else if enableCustomEmojiAsBool, ok := enableCustomEmoji.(bool); !ok {
|
|
||||||
t.Fatal("should've returned ServiceSettings.EnableCustomEmoji as a boolean")
|
|
||||||
} else if !enableCustomEmojiAsBool {
|
|
||||||
t.Fatal("should've returned ServiceSettings.EnableCustomEmoji as true")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := envConfig["TeamSettings"]; ok {
|
|
||||||
t.Fatal("should not have returned TeamSettings")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("as team admin", func(t *testing.T) {
|
|
||||||
TeamAdminClient := th.CreateClient()
|
|
||||||
th.LoginTeamAdminWithClient(TeamAdminClient)
|
|
||||||
|
|
||||||
_, resp := TeamAdminClient.GetEnvironmentConfig()
|
|
||||||
CheckForbiddenStatus(t, resp)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("as regular user", func(t *testing.T) {
|
|
||||||
Client := th.Client
|
|
||||||
|
|
||||||
_, resp := Client.GetEnvironmentConfig()
|
|
||||||
CheckForbiddenStatus(t, resp)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("as not-regular user", func(t *testing.T) {
|
|
||||||
Client := th.CreateClient()
|
|
||||||
|
|
||||||
_, resp := Client.GetEnvironmentConfig()
|
|
||||||
CheckUnauthorizedStatus(t, resp)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetOldClientConfig(t *testing.T) {
|
|
||||||
th := Setup().InitBasic()
|
|
||||||
defer th.TearDown()
|
|
||||||
|
|
||||||
testKey := "supersecretkey"
|
|
||||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.GoogleDeveloperKey = testKey })
|
|
||||||
|
|
||||||
t.Run("with session", func(t *testing.T) {
|
|
||||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
|
||||||
*cfg.ServiceSettings.GoogleDeveloperKey = testKey
|
|
||||||
})
|
|
||||||
|
|
||||||
Client := th.Client
|
|
||||||
|
|
||||||
config, resp := Client.GetOldClientConfig("")
|
|
||||||
CheckNoError(t, resp)
|
|
||||||
|
|
||||||
if len(config["Version"]) == 0 {
|
|
||||||
t.Fatal("config not returned correctly")
|
|
||||||
}
|
|
||||||
|
|
||||||
if config["GoogleDeveloperKey"] != testKey {
|
|
||||||
t.Fatal("config missing developer key")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("without session", func(t *testing.T) {
|
|
||||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
|
||||||
*cfg.ServiceSettings.GoogleDeveloperKey = testKey
|
|
||||||
})
|
|
||||||
|
|
||||||
Client := th.CreateClient()
|
|
||||||
|
|
||||||
config, resp := Client.GetOldClientConfig("")
|
|
||||||
CheckNoError(t, resp)
|
|
||||||
|
|
||||||
if len(config["Version"]) == 0 {
|
|
||||||
t.Fatal("config not returned correctly")
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := config["GoogleDeveloperKey"]; ok {
|
|
||||||
t.Fatal("config should be missing developer key")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("missing format", func(t *testing.T) {
|
|
||||||
Client := th.Client
|
|
||||||
|
|
||||||
if _, err := Client.DoApiGet("/config/client", ""); err == nil || err.StatusCode != http.StatusNotImplemented {
|
|
||||||
t.Fatal("should have errored with 501")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("invalid format", func(t *testing.T) {
|
|
||||||
Client := th.Client
|
|
||||||
|
|
||||||
if _, err := Client.DoApiGet("/config/client?format=junk", ""); err == nil || err.StatusCode != http.StatusBadRequest {
|
|
||||||
t.Fatal("should have errored with 400")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetOldClientLicense(t *testing.T) {
|
|
||||||
th := Setup().InitBasic()
|
|
||||||
defer th.TearDown()
|
|
||||||
Client := th.Client
|
|
||||||
|
|
||||||
license, resp := Client.GetOldClientLicense("")
|
|
||||||
CheckNoError(t, resp)
|
|
||||||
|
|
||||||
if len(license["IsLicensed"]) == 0 {
|
|
||||||
t.Fatal("license not returned correctly")
|
|
||||||
}
|
|
||||||
|
|
||||||
Client.Logout()
|
|
||||||
|
|
||||||
_, resp = Client.GetOldClientLicense("")
|
|
||||||
CheckNoError(t, resp)
|
|
||||||
|
|
||||||
if _, err := Client.DoApiGet("/license/client", ""); err == nil || err.StatusCode != http.StatusNotImplemented {
|
|
||||||
t.Fatal("should have errored with 501")
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := Client.DoApiGet("/license/client?format=junk", ""); err == nil || err.StatusCode != http.StatusBadRequest {
|
|
||||||
t.Fatal("should have errored with 400")
|
|
||||||
}
|
|
||||||
|
|
||||||
license, resp = th.SystemAdminClient.GetOldClientLicense("")
|
|
||||||
CheckNoError(t, resp)
|
|
||||||
|
|
||||||
if len(license["IsLicensed"]) == 0 {
|
|
||||||
t.Fatal("license not returned correctly")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetAudits(t *testing.T) {
|
func TestGetAudits(t *testing.T) {
|
||||||
th := Setup().InitBasic()
|
th := Setup().InitBasic()
|
||||||
defer th.TearDown()
|
defer th.TearDown()
|
||||||
@@ -431,27 +93,38 @@ func TestEmailTest(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
_, resp := Client.TestEmail(&config)
|
t.Run("as system user", func(t *testing.T) {
|
||||||
CheckForbiddenStatus(t, resp)
|
_, resp := Client.TestEmail(&config)
|
||||||
|
CheckForbiddenStatus(t, resp)
|
||||||
|
})
|
||||||
|
|
||||||
_, resp = th.SystemAdminClient.TestEmail(&config)
|
t.Run("as system admin", func(t *testing.T) {
|
||||||
CheckErrorMessage(t, resp, "api.admin.test_email.missing_server")
|
_, resp := th.SystemAdminClient.TestEmail(&config)
|
||||||
CheckBadRequestStatus(t, resp)
|
CheckErrorMessage(t, resp, "api.admin.test_email.missing_server")
|
||||||
|
CheckBadRequestStatus(t, resp)
|
||||||
|
|
||||||
inbucket_host := os.Getenv("CI_INBUCKET_HOST")
|
inbucket_host := os.Getenv("CI_INBUCKET_HOST")
|
||||||
if inbucket_host == "" {
|
if inbucket_host == "" {
|
||||||
inbucket_host = "dockerhost"
|
inbucket_host = "dockerhost"
|
||||||
}
|
}
|
||||||
|
|
||||||
inbucket_port := os.Getenv("CI_INBUCKET_PORT")
|
inbucket_port := os.Getenv("CI_INBUCKET_PORT")
|
||||||
if inbucket_port == "" {
|
if inbucket_port == "" {
|
||||||
inbucket_port = "9000"
|
inbucket_port = "9000"
|
||||||
}
|
}
|
||||||
|
|
||||||
*config.EmailSettings.SMTPServer = inbucket_host
|
*config.EmailSettings.SMTPServer = inbucket_host
|
||||||
*config.EmailSettings.SMTPPort = inbucket_port
|
*config.EmailSettings.SMTPPort = inbucket_port
|
||||||
_, resp = th.SystemAdminClient.TestEmail(&config)
|
_, resp = th.SystemAdminClient.TestEmail(&config)
|
||||||
CheckOKStatus(t, resp)
|
CheckOKStatus(t, resp)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("as restricted system admin", func(t *testing.T) {
|
||||||
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ExperimentalSettings.RestrictSystemAdmin = true })
|
||||||
|
|
||||||
|
_, resp := th.SystemAdminClient.TestEmail(&config)
|
||||||
|
CheckForbiddenStatus(t, resp)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDatabaseRecycle(t *testing.T) {
|
func TestDatabaseRecycle(t *testing.T) {
|
||||||
@@ -459,11 +132,22 @@ func TestDatabaseRecycle(t *testing.T) {
|
|||||||
defer th.TearDown()
|
defer th.TearDown()
|
||||||
Client := th.Client
|
Client := th.Client
|
||||||
|
|
||||||
_, resp := Client.DatabaseRecycle()
|
t.Run("as system user", func(t *testing.T) {
|
||||||
CheckForbiddenStatus(t, resp)
|
_, resp := Client.DatabaseRecycle()
|
||||||
|
CheckForbiddenStatus(t, resp)
|
||||||
|
})
|
||||||
|
|
||||||
_, resp = th.SystemAdminClient.DatabaseRecycle()
|
t.Run("as system admin", func(t *testing.T) {
|
||||||
CheckNoError(t, resp)
|
_, resp := th.SystemAdminClient.DatabaseRecycle()
|
||||||
|
CheckNoError(t, resp)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("as restricted system admin", func(t *testing.T) {
|
||||||
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ExperimentalSettings.RestrictSystemAdmin = true })
|
||||||
|
|
||||||
|
_, resp := th.SystemAdminClient.DatabaseRecycle()
|
||||||
|
CheckForbiddenStatus(t, resp)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidateCaches(t *testing.T) {
|
func TestInvalidateCaches(t *testing.T) {
|
||||||
@@ -471,17 +155,31 @@ func TestInvalidateCaches(t *testing.T) {
|
|||||||
defer th.TearDown()
|
defer th.TearDown()
|
||||||
Client := th.Client
|
Client := th.Client
|
||||||
|
|
||||||
flag, resp := Client.InvalidateCaches()
|
t.Run("as system user", func(t *testing.T) {
|
||||||
CheckForbiddenStatus(t, resp)
|
ok, resp := Client.InvalidateCaches()
|
||||||
if flag {
|
CheckForbiddenStatus(t, resp)
|
||||||
t.Fatal("should not clean the cache due no permission.")
|
if ok {
|
||||||
}
|
t.Fatal("should not clean the cache due no permission.")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
flag, resp = th.SystemAdminClient.InvalidateCaches()
|
t.Run("as system admin", func(t *testing.T) {
|
||||||
CheckNoError(t, resp)
|
ok, resp := th.SystemAdminClient.InvalidateCaches()
|
||||||
if !flag {
|
CheckNoError(t, resp)
|
||||||
t.Fatal("should clean the cache")
|
if !ok {
|
||||||
}
|
t.Fatal("should clean the cache")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("as restricted system admin", func(t *testing.T) {
|
||||||
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ExperimentalSettings.RestrictSystemAdmin = true })
|
||||||
|
|
||||||
|
ok, resp := th.SystemAdminClient.InvalidateCaches()
|
||||||
|
CheckForbiddenStatus(t, resp)
|
||||||
|
if ok {
|
||||||
|
t.Fatal("should not clean the cache due no permission.")
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetLogs(t *testing.T) {
|
func TestGetLogs(t *testing.T) {
|
||||||
@@ -559,42 +257,6 @@ func TestPostLog(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUploadLicenseFile(t *testing.T) {
|
|
||||||
th := Setup().InitBasic()
|
|
||||||
defer th.TearDown()
|
|
||||||
Client := th.Client
|
|
||||||
|
|
||||||
ok, resp := Client.UploadLicenseFile([]byte{})
|
|
||||||
CheckForbiddenStatus(t, resp)
|
|
||||||
if ok {
|
|
||||||
t.Fatal("should fail")
|
|
||||||
}
|
|
||||||
|
|
||||||
ok, resp = th.SystemAdminClient.UploadLicenseFile([]byte{})
|
|
||||||
CheckBadRequestStatus(t, resp)
|
|
||||||
if ok {
|
|
||||||
t.Fatal("should fail")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRemoveLicenseFile(t *testing.T) {
|
|
||||||
th := Setup().InitBasic()
|
|
||||||
defer th.TearDown()
|
|
||||||
Client := th.Client
|
|
||||||
|
|
||||||
ok, resp := Client.RemoveLicenseFile()
|
|
||||||
CheckForbiddenStatus(t, resp)
|
|
||||||
if ok {
|
|
||||||
t.Fatal("should fail")
|
|
||||||
}
|
|
||||||
|
|
||||||
ok, resp = th.SystemAdminClient.RemoveLicenseFile()
|
|
||||||
CheckNoError(t, resp)
|
|
||||||
if !ok {
|
|
||||||
t.Fatal("should pass")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetAnalyticsOld(t *testing.T) {
|
func TestGetAnalyticsOld(t *testing.T) {
|
||||||
th := Setup().InitBasic()
|
th := Setup().InitBasic()
|
||||||
defer th.TearDown()
|
defer th.TearDown()
|
||||||
@@ -696,34 +358,45 @@ func TestS3TestConnection(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
_, resp := Client.TestS3Connection(&config)
|
t.Run("as system user", func(t *testing.T) {
|
||||||
CheckForbiddenStatus(t, resp)
|
_, resp := Client.TestS3Connection(&config)
|
||||||
|
CheckForbiddenStatus(t, resp)
|
||||||
|
})
|
||||||
|
|
||||||
_, resp = th.SystemAdminClient.TestS3Connection(&config)
|
t.Run("as system admin", func(t *testing.T) {
|
||||||
CheckBadRequestStatus(t, resp)
|
_, resp := th.SystemAdminClient.TestS3Connection(&config)
|
||||||
if resp.Error.Message != "S3 Bucket is required" {
|
CheckBadRequestStatus(t, resp)
|
||||||
t.Fatal("should return error - missing s3 bucket")
|
if resp.Error.Message != "S3 Bucket is required" {
|
||||||
}
|
t.Fatal("should return error - missing s3 bucket")
|
||||||
|
}
|
||||||
|
|
||||||
// If this fails, check the test configuration to ensure minio is setup with the
|
// If this fails, check the test configuration to ensure minio is setup with the
|
||||||
// `mattermost-test` bucket defined by model.MINIO_BUCKET.
|
// `mattermost-test` bucket defined by model.MINIO_BUCKET.
|
||||||
*config.FileSettings.AmazonS3Bucket = model.MINIO_BUCKET
|
*config.FileSettings.AmazonS3Bucket = model.MINIO_BUCKET
|
||||||
*config.FileSettings.AmazonS3Region = "us-east-1"
|
*config.FileSettings.AmazonS3Region = "us-east-1"
|
||||||
_, resp = th.SystemAdminClient.TestS3Connection(&config)
|
_, resp = th.SystemAdminClient.TestS3Connection(&config)
|
||||||
CheckOKStatus(t, resp)
|
CheckOKStatus(t, resp)
|
||||||
|
|
||||||
config.FileSettings.AmazonS3Region = model.NewString("")
|
config.FileSettings.AmazonS3Region = model.NewString("")
|
||||||
_, resp = th.SystemAdminClient.TestS3Connection(&config)
|
_, resp = th.SystemAdminClient.TestS3Connection(&config)
|
||||||
CheckOKStatus(t, resp)
|
CheckOKStatus(t, resp)
|
||||||
|
|
||||||
config.FileSettings.AmazonS3Bucket = model.NewString("Wrong_bucket")
|
config.FileSettings.AmazonS3Bucket = model.NewString("Wrong_bucket")
|
||||||
_, resp = th.SystemAdminClient.TestS3Connection(&config)
|
_, resp = th.SystemAdminClient.TestS3Connection(&config)
|
||||||
CheckInternalErrorStatus(t, resp)
|
CheckInternalErrorStatus(t, resp)
|
||||||
assert.Equal(t, "Unable to create bucket.", resp.Error.Message)
|
assert.Equal(t, "Unable to create bucket.", resp.Error.Message)
|
||||||
|
|
||||||
*config.FileSettings.AmazonS3Bucket = "shouldcreatenewbucket"
|
*config.FileSettings.AmazonS3Bucket = "shouldcreatenewbucket"
|
||||||
_, resp = th.SystemAdminClient.TestS3Connection(&config)
|
_, resp = th.SystemAdminClient.TestS3Connection(&config)
|
||||||
CheckOKStatus(t, resp)
|
CheckOKStatus(t, resp)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("as restricted system admin", func(t *testing.T) {
|
||||||
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ExperimentalSettings.RestrictSystemAdmin = true })
|
||||||
|
|
||||||
|
_, resp := th.SystemAdminClient.TestS3Connection(&config)
|
||||||
|
CheckForbiddenStatus(t, resp)
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
39
app/admin.go
39
app/admin.go
@@ -12,12 +12,10 @@ import (
|
|||||||
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost-server/config"
|
|
||||||
"github.com/mattermost/mattermost-server/mlog"
|
"github.com/mattermost/mattermost-server/mlog"
|
||||||
"github.com/mattermost/mattermost-server/model"
|
"github.com/mattermost/mattermost-server/model"
|
||||||
"github.com/mattermost/mattermost-server/services/mailservice"
|
"github.com/mattermost/mattermost-server/services/mailservice"
|
||||||
"github.com/mattermost/mattermost-server/utils"
|
"github.com/mattermost/mattermost-server/utils"
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (a *App) GetLogs(page, perPage int) ([]string, *model.AppError) {
|
func (a *App) GetLogs(page, perPage int) ([]string, *model.AppError) {
|
||||||
@@ -150,43 +148,6 @@ func (a *App) InvalidateAllCachesSkipSend() {
|
|||||||
a.LoadLicense()
|
a.LoadLicense()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) GetSanitizedConfig() *model.Config {
|
|
||||||
cfg := a.Config().Clone()
|
|
||||||
cfg.Sanitize()
|
|
||||||
|
|
||||||
return cfg
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *App) GetEnvironmentConfig() map[string]interface{} {
|
|
||||||
return a.EnvironmentConfig()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *App) SaveConfig(newCfg *model.Config, sendConfigChangeClusterMessage bool) *model.AppError {
|
|
||||||
oldCfg, err := a.Srv.configStore.Set(newCfg)
|
|
||||||
if errors.Cause(err) == config.ErrReadOnlyConfiguration {
|
|
||||||
return model.NewAppError("saveConfig", "ent.cluster.save_config.error", nil, err.Error(), http.StatusForbidden)
|
|
||||||
} else if err != nil {
|
|
||||||
return model.NewAppError("saveConfig", "app.save_config.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
|
|
||||||
if a.Metrics != nil {
|
|
||||||
if *a.Config().MetricsSettings.Enable {
|
|
||||||
a.Metrics.StartServer()
|
|
||||||
} else {
|
|
||||||
a.Metrics.StopServer()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if a.Cluster != nil {
|
|
||||||
err := a.Cluster.ConfigChanged(oldCfg, newCfg, sendConfigChangeClusterMessage)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *App) RecycleDatabaseConnection() {
|
func (a *App) RecycleDatabaseConnection() {
|
||||||
oldStore := a.Srv.Store
|
oldStore := a.Srv.Store
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -358,3 +359,43 @@ func (a *App) GetConfigFile(name string) ([]byte, error) {
|
|||||||
|
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetSanitizedConfig gets the configuration for a system admin without any secrets.
|
||||||
|
func (a *App) GetSanitizedConfig() *model.Config {
|
||||||
|
cfg := a.Config().Clone()
|
||||||
|
cfg.Sanitize()
|
||||||
|
|
||||||
|
return cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetEnvironmentConfig returns a map of configuration keys whose values have been overridden by an environment variable.
|
||||||
|
func (a *App) GetEnvironmentConfig() map[string]interface{} {
|
||||||
|
return a.EnvironmentConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveConfig replaces the active configuration, optionally notifying cluster peers.
|
||||||
|
func (a *App) SaveConfig(newCfg *model.Config, sendConfigChangeClusterMessage bool) *model.AppError {
|
||||||
|
oldCfg, err := a.Srv.configStore.Set(newCfg)
|
||||||
|
if errors.Cause(err) == config.ErrReadOnlyConfiguration {
|
||||||
|
return model.NewAppError("saveConfig", "ent.cluster.save_config.error", nil, err.Error(), http.StatusForbidden)
|
||||||
|
} else if err != nil {
|
||||||
|
return model.NewAppError("saveConfig", "app.save_config.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.Metrics != nil {
|
||||||
|
if *a.Config().MetricsSettings.Enable {
|
||||||
|
a.Metrics.StartServer()
|
||||||
|
} else {
|
||||||
|
a.Metrics.StopServer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.Cluster != nil {
|
||||||
|
err := a.Cluster.ConfigChanged(oldCfg, newCfg, sendConfigChangeClusterMessage)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -359,7 +359,8 @@
|
|||||||
"ClientSideCertEnable": false,
|
"ClientSideCertEnable": false,
|
||||||
"ClientSideCertCheck": "secondary",
|
"ClientSideCertCheck": "secondary",
|
||||||
"DisablePostMetadata": false,
|
"DisablePostMetadata": false,
|
||||||
"LinkMetadataTimeoutMilliseconds": 5000
|
"LinkMetadataTimeoutMilliseconds": 5000,
|
||||||
|
"RestrictSystemAdmin": false
|
||||||
},
|
},
|
||||||
"AnalyticsSettings": {
|
"AnalyticsSettings": {
|
||||||
"MaxUsersForStatistics": 2500
|
"MaxUsersForStatistics": 2500
|
||||||
|
|||||||
@@ -55,6 +55,10 @@
|
|||||||
"id": "api.admin.add_certificate.array.app_error",
|
"id": "api.admin.add_certificate.array.app_error",
|
||||||
"translation": "No file under 'certificate' in request."
|
"translation": "No file under 'certificate' in request."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "api.restricted_system_admin",
|
||||||
|
"translation": "This action is forbidden to a restricted system admin."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "app.submit_interactive_dialog.json_error",
|
"id": "app.submit_interactive_dialog.json_error",
|
||||||
"translation": "Encountered an error encoding JSON for the interactive dialog."
|
"translation": "Encountered an error encoding JSON for the interactive dialog."
|
||||||
|
|||||||
@@ -713,6 +713,7 @@ type ExperimentalSettings struct {
|
|||||||
ClientSideCertCheck *string
|
ClientSideCertCheck *string
|
||||||
DisablePostMetadata *bool
|
DisablePostMetadata *bool
|
||||||
LinkMetadataTimeoutMilliseconds *int64
|
LinkMetadataTimeoutMilliseconds *int64
|
||||||
|
RestrictSystemAdmin *bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ExperimentalSettings) SetDefaults() {
|
func (s *ExperimentalSettings) SetDefaults() {
|
||||||
@@ -731,6 +732,10 @@ func (s *ExperimentalSettings) SetDefaults() {
|
|||||||
if s.LinkMetadataTimeoutMilliseconds == nil {
|
if s.LinkMetadataTimeoutMilliseconds == nil {
|
||||||
s.LinkMetadataTimeoutMilliseconds = NewInt64(EXPERIMENTAL_SETTINGS_DEFAULT_LINK_METADATA_TIMEOUT_MILLISECONDS)
|
s.LinkMetadataTimeoutMilliseconds = NewInt64(EXPERIMENTAL_SETTINGS_DEFAULT_LINK_METADATA_TIMEOUT_MILLISECONDS)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if s.RestrictSystemAdmin == nil {
|
||||||
|
s.RestrictSystemAdmin = NewBool(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type AnalyticsSettings struct {
|
type AnalyticsSettings struct {
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user