[MM-19720] Expose endpoint for config patch (#12997)
Этот коммит содержится в:
коммит произвёл
Ben Schumacher
родитель
69f4dcd955
Коммит
51a61f6bc1
@@ -15,6 +15,7 @@ import (
|
||||
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/patch", api.ApiSessionRequired(patchConfig)).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")
|
||||
@@ -81,19 +82,7 @@ func updateConfig(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
// Do not allow plugin uploads to be toggled through the API
|
||||
cfg.PluginSettings.EnableUploads = appCfg.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 != *appCfg.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)
|
||||
}
|
||||
}
|
||||
c.App.HandleMessageExportConfig(cfg, appCfg)
|
||||
|
||||
err := cfg.IsValid()
|
||||
if err != nil {
|
||||
@@ -149,3 +138,59 @@ func getEnvironmentConfig(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
|
||||
w.Write([]byte(model.StringInterfaceToJson(envConfig)))
|
||||
}
|
||||
|
||||
func patchConfig(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
|
||||
}
|
||||
|
||||
appCfg := c.App.Config()
|
||||
var filterFn utils.StructFieldFilter
|
||||
if *appCfg.ExperimentalSettings.RestrictSystemAdmin {
|
||||
filterFn = func(structField reflect.StructField, base, patch reflect.Value) bool {
|
||||
return !(structField.Tag.Get("restricted") == "true")
|
||||
}
|
||||
} else {
|
||||
filterFn = func(structField reflect.StructField, base, patch reflect.Value) bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// Do not allow plugin uploads to be toggled through the API
|
||||
cfg.PluginSettings.EnableUploads = appCfg.PluginSettings.EnableUploads
|
||||
|
||||
if cfg.MessageExportSettings.EnableExport != nil {
|
||||
c.App.HandleMessageExportConfig(cfg, appCfg)
|
||||
}
|
||||
|
||||
updatedCfg, mergeErr := config.Merge(appCfg, cfg, &utils.MergeConfig{
|
||||
StructFieldFilter: filterFn,
|
||||
})
|
||||
|
||||
if mergeErr != nil {
|
||||
c.Err = model.NewAppError("patchConfig", "api.config.update_config.restricted_merge.app_error", nil, mergeErr.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
err := updatedCfg.IsValid()
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
err = c.App.SaveConfig(updatedCfg, true)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
|
||||
w.Write([]byte(c.App.GetSanitizedConfig().ToJson()))
|
||||
}
|
||||
|
||||
@@ -345,3 +345,100 @@ func TestGetOldClientConfig(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestPatchConfig(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
client := th.Client
|
||||
|
||||
t.Run("config is missing", func(t *testing.T) {
|
||||
_, response := client.PatchConfig(nil)
|
||||
CheckBadRequestStatus(t, response)
|
||||
})
|
||||
|
||||
t.Run("user is not system admin", func(t *testing.T) {
|
||||
_, response := client.PatchConfig(&model.Config{})
|
||||
CheckForbiddenStatus(t, response)
|
||||
})
|
||||
|
||||
t.Run("should not update the restricted fields when restrict toggle is on", func(t *testing.T) {
|
||||
*th.App.Config().ExperimentalSettings.RestrictSystemAdmin = true
|
||||
|
||||
config := model.Config{LogSettings: model.LogSettings{
|
||||
ConsoleLevel: model.NewString("INFO"),
|
||||
}}
|
||||
|
||||
updatedConfig, _ := th.SystemAdminClient.PatchConfig(&config)
|
||||
|
||||
assert.Equal(t, "DEBUG", *updatedConfig.LogSettings.ConsoleLevel)
|
||||
})
|
||||
|
||||
t.Run("check if config is valid", func(t *testing.T) {
|
||||
config := model.Config{PasswordSettings: model.PasswordSettings{
|
||||
MinimumLength: model.NewInt(4),
|
||||
}}
|
||||
|
||||
_, response := th.SystemAdminClient.PatchConfig(&config)
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, response.StatusCode)
|
||||
assert.NotNil(t, response.Error)
|
||||
assert.Equal(t, "model.config.is_valid.password_length.app_error", response.Error.Id)
|
||||
})
|
||||
|
||||
t.Run("should patch the config", func(t *testing.T) {
|
||||
*th.App.Config().ExperimentalSettings.RestrictSystemAdmin = false
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.TeamSettings.ExperimentalDefaultChannels = []string{"some-channel"} })
|
||||
|
||||
oldConfig, _ := th.SystemAdminClient.GetConfig()
|
||||
|
||||
assert.False(t, *oldConfig.PasswordSettings.Lowercase)
|
||||
assert.NotEqual(t, 15, *oldConfig.PasswordSettings.MinimumLength)
|
||||
assert.Equal(t, "DEBUG", *oldConfig.LogSettings.ConsoleLevel)
|
||||
assert.True(t, oldConfig.PluginSettings.PluginStates["com.mattermost.nps"].Enable)
|
||||
|
||||
states := make(map[string]*model.PluginState)
|
||||
states["com.mattermost.nps"] = &model.PluginState{Enable: *model.NewBool(false)}
|
||||
config := model.Config{PasswordSettings: model.PasswordSettings{
|
||||
Lowercase: model.NewBool(true),
|
||||
MinimumLength: model.NewInt(15),
|
||||
}, LogSettings: model.LogSettings{
|
||||
ConsoleLevel: model.NewString("INFO"),
|
||||
},
|
||||
TeamSettings: model.TeamSettings{
|
||||
ExperimentalDefaultChannels: []string{"another-channel"},
|
||||
},
|
||||
PluginSettings: model.PluginSettings{
|
||||
PluginStates: states,
|
||||
},
|
||||
}
|
||||
|
||||
_, response := th.SystemAdminClient.PatchConfig(&config)
|
||||
|
||||
updatedConfig, _ := th.SystemAdminClient.GetConfig()
|
||||
assert.True(t, *updatedConfig.PasswordSettings.Lowercase)
|
||||
assert.Equal(t, "INFO", *updatedConfig.LogSettings.ConsoleLevel)
|
||||
assert.Equal(t, []string{"another-channel"}, updatedConfig.TeamSettings.ExperimentalDefaultChannels)
|
||||
assert.False(t, updatedConfig.PluginSettings.PluginStates["com.mattermost.nps"].Enable)
|
||||
assert.Equal(t, "no-cache, no-store, must-revalidate", response.Header.Get("Cache-Control"))
|
||||
})
|
||||
|
||||
t.Run("should sanitize config", func(t *testing.T) {
|
||||
config := model.Config{PasswordSettings: model.PasswordSettings{
|
||||
Symbol: model.NewBool(true),
|
||||
}}
|
||||
|
||||
updatedConfig, _ := th.SystemAdminClient.PatchConfig(&config)
|
||||
|
||||
assert.Equal(t, model.FAKE_SETTING, *updatedConfig.SqlSettings.DataSource)
|
||||
})
|
||||
|
||||
t.Run("not allowing to toggle enable uploads for plugin via api", func(t *testing.T) {
|
||||
config := model.Config{PluginSettings: model.PluginSettings{
|
||||
EnableUploads: model.NewBool(true),
|
||||
}}
|
||||
|
||||
updatedConfig, _ := th.SystemAdminClient.PatchConfig(&config)
|
||||
|
||||
assert.Equal(t, false, *updatedConfig.PluginSettings.EnableUploads)
|
||||
})
|
||||
}
|
||||
|
||||
Ссылка в новой задаче
Block a user