From 65f03de1ee49f831f12dd9bbf28a512f03d6e80f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Villablanca=20V=C3=A1squez?= Date: Tue, 3 Sep 2019 18:41:52 -0400 Subject: [PATCH] [MM-19711] - Get plugin configuration without removing secrets (#11928) * Method GetUnsanitizedConfig() exposed to API (Plugin) interface and his implementations * improvements with some suggestions * Fix documentation (final period added) Co-Authored-By: Ali Farooq <25732808+ali-farooq0@users.noreply.github.com> * Added some test for Plugin.GetConfig and Plugin.GetUnsanitizedConfig * Removed empty lines --- app/plugin_api.go | 5 +++ app/plugin_api_test.go | 74 ++++++++++++++++++++++++++++++++++ plugin/api.go | 5 +++ plugin/client_rpc_generated.go | 27 +++++++++++++ plugin/plugintest/api.go | 22 +++++++++- plugin/plugintest/helpers.go | 6 ++- plugin/plugintest/hooks.go | 15 ++++--- 7 files changed, 145 insertions(+), 9 deletions(-) diff --git a/app/plugin_api.go b/app/plugin_api.go index a99067fcb4..05ca69ab8b 100644 --- a/app/plugin_api.go +++ b/app/plugin_api.go @@ -81,6 +81,11 @@ func (api *PluginAPI) GetConfig() *model.Config { return api.app.GetSanitizedConfig() } +// GetUnsanitizedConfig gets the configuration for a system admin without removing secrets. +func (api *PluginAPI) GetUnsanitizedConfig() *model.Config { + return api.app.Config().Clone() +} + func (api *PluginAPI) SaveConfig(config *model.Config) *model.AppError { return api.app.SaveConfig(config, true) } diff --git a/app/plugin_api_test.go b/app/plugin_api_test.go index 5f3a756016..8277a2897b 100644 --- a/app/plugin_api_test.go +++ b/app/plugin_api_test.go @@ -1331,3 +1331,77 @@ func TestPluginCreatePostWithUploadedFile(t *testing.T) { require.Nil(t, err) assert.Equal(t, model.StringArray{fileInfo.Id}, actualPost.FileIds) } + +func TestPluginAPIGetConfig(t *testing.T) { + th := Setup(t).InitBasic() + defer th.TearDown() + api := th.SetupPluginAPI() + + config := api.GetConfig() + if config.LdapSettings.BindPassword != nil && len(*config.LdapSettings.BindPassword) > 0 { + assert.Equal(t, *config.LdapSettings.BindPassword, model.FAKE_SETTING) + } + + assert.Equal(t, *config.FileSettings.PublicLinkSalt, model.FAKE_SETTING) + + if len(*config.FileSettings.AmazonS3SecretAccessKey) > 0 { + assert.Equal(t, *config.FileSettings.AmazonS3SecretAccessKey, model.FAKE_SETTING) + } + + if config.EmailSettings.SMTPPassword != nil && len(*config.EmailSettings.SMTPPassword) > 0 { + assert.Equal(t, *config.EmailSettings.SMTPPassword, model.FAKE_SETTING) + } + + if len(*config.GitLabSettings.Secret) > 0 { + assert.Equal(t, *config.GitLabSettings.Secret, model.FAKE_SETTING) + } + + assert.Equal(t, *config.SqlSettings.DataSource, model.FAKE_SETTING) + assert.Equal(t, *config.SqlSettings.AtRestEncryptKey, model.FAKE_SETTING) + assert.Equal(t, *config.ElasticsearchSettings.Password, model.FAKE_SETTING) + + for i := range config.SqlSettings.DataSourceReplicas { + assert.Equal(t, config.SqlSettings.DataSourceReplicas[i], model.FAKE_SETTING) + } + + for i := range config.SqlSettings.DataSourceSearchReplicas { + assert.Equal(t, config.SqlSettings.DataSourceSearchReplicas[i], model.FAKE_SETTING) + } +} + +func TestPluginAPIGetUnsanitizedConfig(t *testing.T) { + th := Setup(t).InitBasic() + defer th.TearDown() + api := th.SetupPluginAPI() + + config := api.GetUnsanitizedConfig() + if config.LdapSettings.BindPassword != nil && len(*config.LdapSettings.BindPassword) > 0 { + assert.NotEqual(t, *config.LdapSettings.BindPassword, model.FAKE_SETTING) + } + + assert.NotEqual(t, *config.FileSettings.PublicLinkSalt, model.FAKE_SETTING) + + if len(*config.FileSettings.AmazonS3SecretAccessKey) > 0 { + assert.NotEqual(t, *config.FileSettings.AmazonS3SecretAccessKey, model.FAKE_SETTING) + } + + if config.EmailSettings.SMTPPassword != nil && len(*config.EmailSettings.SMTPPassword) > 0 { + assert.NotEqual(t, *config.EmailSettings.SMTPPassword, model.FAKE_SETTING) + } + + if len(*config.GitLabSettings.Secret) > 0 { + assert.NotEqual(t, *config.GitLabSettings.Secret, model.FAKE_SETTING) + } + + assert.NotEqual(t, *config.SqlSettings.DataSource, model.FAKE_SETTING) + assert.NotEqual(t, *config.SqlSettings.AtRestEncryptKey, model.FAKE_SETTING) + assert.NotEqual(t, *config.ElasticsearchSettings.Password, model.FAKE_SETTING) + + for i := range config.SqlSettings.DataSourceReplicas { + assert.NotEqual(t, config.SqlSettings.DataSourceReplicas[i], model.FAKE_SETTING) + } + + for i := range config.SqlSettings.DataSourceSearchReplicas { + assert.NotEqual(t, config.SqlSettings.DataSourceSearchReplicas[i], model.FAKE_SETTING) + } +} diff --git a/plugin/api.go b/plugin/api.go index fed0bbb3df..c799b47f57 100644 --- a/plugin/api.go +++ b/plugin/api.go @@ -31,6 +31,11 @@ type API interface { // GetConfig fetches the currently persisted config GetConfig() *model.Config + // GetUnsanitizedConfig fetches the currently persisted config without removing secrets. + // + // Minimum server version: 5.16 + GetUnsanitizedConfig() *model.Config + // SaveConfig sets the given config and persists the changes SaveConfig(config *model.Config) *model.AppError diff --git a/plugin/client_rpc_generated.go b/plugin/client_rpc_generated.go index 22b577f3c5..e1bd6165ff 100644 --- a/plugin/client_rpc_generated.go +++ b/plugin/client_rpc_generated.go @@ -588,6 +588,33 @@ func (s *apiRPCServer) GetConfig(args *Z_GetConfigArgs, returns *Z_GetConfigRetu return nil } +type Z_GetUnsanitizedConfigArgs struct { +} + +type Z_GetUnsanitizedConfigReturns struct { + A *model.Config +} + +func (g *apiRPCClient) GetUnsanitizedConfig() *model.Config { + _args := &Z_GetUnsanitizedConfigArgs{} + _returns := &Z_GetUnsanitizedConfigReturns{} + if err := g.client.Call("Plugin.GetUnsanitizedConfig", _args, _returns); err != nil { + log.Printf("RPC call to GetUnsanitizedConfig API failed: %s", err.Error()) + } + return _returns.A +} + +func (s *apiRPCServer) GetUnsanitizedConfig(args *Z_GetUnsanitizedConfigArgs, returns *Z_GetUnsanitizedConfigReturns) error { + if hook, ok := s.impl.(interface { + GetUnsanitizedConfig() *model.Config + }); ok { + returns.A = hook.GetUnsanitizedConfig() + } else { + return encodableError(fmt.Errorf("API GetUnsanitizedConfig called but not implemented.")) + } + return nil +} + type Z_SaveConfigArgs struct { A *model.Config } diff --git a/plugin/plugintest/api.go b/plugin/plugintest/api.go index f634748a1e..e5f92671b2 100644 --- a/plugin/plugintest/api.go +++ b/plugin/plugintest/api.go @@ -4,8 +4,10 @@ package plugintest -import mock "github.com/stretchr/testify/mock" -import model "github.com/mattermost/mattermost-server/model" +import ( + model "github.com/mattermost/mattermost-server/model" + mock "github.com/stretchr/testify/mock" +) // API is an autogenerated mock type for the API type type API struct { @@ -1636,6 +1638,22 @@ func (_m *API) GetTeamsUnreadForUser(userId string) ([]*model.TeamUnread, *model return r0, r1 } +// GetUnsanitizedConfig provides a mock function with given fields: +func (_m *API) GetUnsanitizedConfig() *model.Config { + ret := _m.Called() + + var r0 *model.Config + if rf, ok := ret.Get(0).(func() *model.Config); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.Config) + } + } + + return r0 +} + // GetUser provides a mock function with given fields: userId func (_m *API) GetUser(userId string) (*model.User, *model.AppError) { ret := _m.Called(userId) diff --git a/plugin/plugintest/helpers.go b/plugin/plugintest/helpers.go index ba3f92b33a..066c0b2b8c 100644 --- a/plugin/plugintest/helpers.go +++ b/plugin/plugintest/helpers.go @@ -4,8 +4,10 @@ package plugintest -import mock "github.com/stretchr/testify/mock" -import model "github.com/mattermost/mattermost-server/model" +import ( + model "github.com/mattermost/mattermost-server/model" + mock "github.com/stretchr/testify/mock" +) // Helpers is an autogenerated mock type for the Helpers type type Helpers struct { diff --git a/plugin/plugintest/hooks.go b/plugin/plugintest/hooks.go index 5ebeecc42e..ef317bf4e2 100644 --- a/plugin/plugintest/hooks.go +++ b/plugin/plugintest/hooks.go @@ -4,11 +4,16 @@ package plugintest -import http "net/http" -import io "io" -import mock "github.com/stretchr/testify/mock" -import model "github.com/mattermost/mattermost-server/model" -import plugin "github.com/mattermost/mattermost-server/plugin" +import ( + io "io" + http "net/http" + + mock "github.com/stretchr/testify/mock" + + model "github.com/mattermost/mattermost-server/model" + + plugin "github.com/mattermost/mattermost-server/plugin" +) // Hooks is an autogenerated mock type for the Hooks type type Hooks struct {