github.com/masterhung0112/hk_server/v5@v5.0.0-20220302090640-ec71aef15e1c/api4/config_test.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See LICENSE.txt for license information.
     3  
     4  package api4
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"io/ioutil"
    10  	"net/http"
    11  	"os"
    12  	"strings"
    13  	"testing"
    14  
    15  	"github.com/stretchr/testify/assert"
    16  	"github.com/stretchr/testify/require"
    17  
    18  	"github.com/masterhung0112/hk_server/v5/app"
    19  	"github.com/masterhung0112/hk_server/v5/config"
    20  	"github.com/masterhung0112/hk_server/v5/model"
    21  )
    22  
    23  func TestGetConfig(t *testing.T) {
    24  	th := Setup(t)
    25  	defer th.TearDown()
    26  	Client := th.Client
    27  
    28  	_, resp := Client.GetConfig()
    29  	CheckForbiddenStatus(t, resp)
    30  
    31  	th.TestForSystemAdminAndLocal(t, func(t *testing.T, client *model.Client4) {
    32  		cfg, resp := client.GetConfig()
    33  		CheckNoError(t, resp)
    34  
    35  		require.NotEqual(t, "", cfg.TeamSettings.SiteName)
    36  
    37  		if *cfg.LdapSettings.BindPassword != model.FAKE_SETTING && *cfg.LdapSettings.BindPassword != "" {
    38  			require.FailNow(t, "did not sanitize properly")
    39  		}
    40  		require.Equal(t, model.FAKE_SETTING, *cfg.FileSettings.PublicLinkSalt, "did not sanitize properly")
    41  
    42  		if *cfg.FileSettings.AmazonS3SecretAccessKey != model.FAKE_SETTING && *cfg.FileSettings.AmazonS3SecretAccessKey != "" {
    43  			require.FailNow(t, "did not sanitize properly")
    44  		}
    45  		if *cfg.EmailSettings.SMTPPassword != model.FAKE_SETTING && *cfg.EmailSettings.SMTPPassword != "" {
    46  			require.FailNow(t, "did not sanitize properly")
    47  		}
    48  		if *cfg.GitLabSettings.Secret != model.FAKE_SETTING && *cfg.GitLabSettings.Secret != "" {
    49  			require.FailNow(t, "did not sanitize properly")
    50  		}
    51  		require.Equal(t, model.FAKE_SETTING, *cfg.SqlSettings.DataSource, "did not sanitize properly")
    52  		require.Equal(t, model.FAKE_SETTING, *cfg.SqlSettings.AtRestEncryptKey, "did not sanitize properly")
    53  		if !strings.Contains(strings.Join(cfg.SqlSettings.DataSourceReplicas, " "), model.FAKE_SETTING) && len(cfg.SqlSettings.DataSourceReplicas) != 0 {
    54  			require.FailNow(t, "did not sanitize properly")
    55  		}
    56  		if !strings.Contains(strings.Join(cfg.SqlSettings.DataSourceSearchReplicas, " "), model.FAKE_SETTING) && len(cfg.SqlSettings.DataSourceSearchReplicas) != 0 {
    57  			require.FailNow(t, "did not sanitize properly")
    58  		}
    59  	})
    60  }
    61  
    62  func TestGetConfigWithAccessTag(t *testing.T) {
    63  	th := Setup(t)
    64  	defer th.TearDown()
    65  
    66  	varyByHeader := *&th.App.Config().RateLimitSettings.VaryByHeader // environment perm.
    67  	supportEmail := *&th.App.Config().SupportSettings.SupportEmail   // site perm.
    68  	defer th.App.UpdateConfig(func(cfg *model.Config) {
    69  		cfg.RateLimitSettings.VaryByHeader = varyByHeader
    70  		cfg.SupportSettings.SupportEmail = supportEmail
    71  	})
    72  
    73  	// set some values so that we know they're not blank
    74  	mockVaryByHeader := model.NewId()
    75  	mockSupportEmail := model.NewId() + "@mattermost.com"
    76  	th.App.UpdateConfig(func(cfg *model.Config) {
    77  		cfg.RateLimitSettings.VaryByHeader = mockVaryByHeader
    78  		cfg.SupportSettings.SupportEmail = &mockSupportEmail
    79  	})
    80  
    81  	th.Client.Login(th.BasicUser.Username, th.BasicUser.Password)
    82  
    83  	// add read sysconsole environment config
    84  	th.AddPermissionToRole(model.PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_RATE_LIMITING.Id, model.SYSTEM_USER_ROLE_ID)
    85  	defer th.RemovePermissionFromRole(model.PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_RATE_LIMITING.Id, model.SYSTEM_USER_ROLE_ID)
    86  
    87  	cfg, resp := th.Client.GetConfig()
    88  	CheckNoError(t, resp)
    89  
    90  	t.Run("Cannot read value without permission", func(t *testing.T) {
    91  		assert.Nil(t, cfg.SupportSettings.SupportEmail)
    92  	})
    93  
    94  	t.Run("Can read value with permission", func(t *testing.T) {
    95  		assert.Equal(t, mockVaryByHeader, cfg.RateLimitSettings.VaryByHeader)
    96  	})
    97  
    98  	t.Run("Contains Feature Flags", func(t *testing.T) {
    99  		assert.NotNil(t, cfg.FeatureFlags)
   100  	})
   101  }
   102  
   103  func TestGetConfigAnyFlagsAccess(t *testing.T) {
   104  	th := Setup(t)
   105  	defer th.TearDown()
   106  
   107  	th.Client.Login(th.BasicUser.Username, th.BasicUser.Password)
   108  	_, resp := th.Client.GetConfig()
   109  
   110  	t.Run("Check permissions error with no sysconsole read permission", func(t *testing.T) {
   111  		CheckForbiddenStatus(t, resp)
   112  	})
   113  
   114  	// add read sysconsole environment config
   115  	th.AddPermissionToRole(model.PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_RATE_LIMITING.Id, model.SYSTEM_USER_ROLE_ID)
   116  	defer th.RemovePermissionFromRole(model.PERMISSION_SYSCONSOLE_READ_ENVIRONMENT_RATE_LIMITING.Id, model.SYSTEM_USER_ROLE_ID)
   117  
   118  	cfg, resp := th.Client.GetConfig()
   119  	CheckNoError(t, resp)
   120  	t.Run("Can read value with permission", func(t *testing.T) {
   121  		assert.NotNil(t, cfg.FeatureFlags)
   122  	})
   123  }
   124  
   125  func TestReloadConfig(t *testing.T) {
   126  	th := Setup(t)
   127  	defer th.TearDown()
   128  	Client := th.Client
   129  
   130  	t.Run("as system user", func(t *testing.T) {
   131  		ok, resp := Client.ReloadConfig()
   132  		CheckForbiddenStatus(t, resp)
   133  		require.False(t, ok, "should not Reload the config due no permission.")
   134  	})
   135  
   136  	t.Run("as system admin", func(t *testing.T) {
   137  		ok, resp := th.SystemAdminClient.ReloadConfig()
   138  		CheckNoError(t, resp)
   139  		require.True(t, ok, "should Reload the config")
   140  	})
   141  
   142  	t.Run("as restricted system admin", func(t *testing.T) {
   143  		th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ExperimentalSettings.RestrictSystemAdmin = true })
   144  
   145  		ok, resp := Client.ReloadConfig()
   146  		CheckForbiddenStatus(t, resp)
   147  		require.False(t, ok, "should not Reload the config due no permission.")
   148  	})
   149  }
   150  
   151  func TestUpdateConfig(t *testing.T) {
   152  	th := Setup(t)
   153  	defer th.TearDown()
   154  	Client := th.Client
   155  
   156  	cfg, resp := th.SystemAdminClient.GetConfig()
   157  	CheckNoError(t, resp)
   158  
   159  	_, resp = Client.UpdateConfig(cfg)
   160  	CheckForbiddenStatus(t, resp)
   161  
   162  	th.TestForSystemAdminAndLocal(t, func(t *testing.T, client *model.Client4) {
   163  		SiteName := th.App.Config().TeamSettings.SiteName
   164  
   165  		*cfg.TeamSettings.SiteName = "MyFancyName"
   166  		cfg, resp = client.UpdateConfig(cfg)
   167  		CheckNoError(t, resp)
   168  
   169  		require.Equal(t, "MyFancyName", *cfg.TeamSettings.SiteName, "It should update the SiteName")
   170  
   171  		//Revert the change
   172  		cfg.TeamSettings.SiteName = SiteName
   173  		cfg, resp = client.UpdateConfig(cfg)
   174  		CheckNoError(t, resp)
   175  
   176  		require.Equal(t, SiteName, cfg.TeamSettings.SiteName, "It should update the SiteName")
   177  
   178  		t.Run("Should set defaults for missing fields", func(t *testing.T) {
   179  			_, appErr := th.SystemAdminClient.DoApiPut(th.SystemAdminClient.GetConfigRoute(), "{}")
   180  			require.Nil(t, appErr)
   181  		})
   182  
   183  		t.Run("Should fail with validation error if invalid config setting is passed", func(t *testing.T) {
   184  			//Revert the change
   185  			badcfg := cfg.Clone()
   186  			badcfg.PasswordSettings.MinimumLength = model.NewInt(4)
   187  			badcfg.PasswordSettings.MinimumLength = model.NewInt(4)
   188  			_, resp = client.UpdateConfig(badcfg)
   189  			CheckBadRequestStatus(t, resp)
   190  			CheckErrorMessage(t, resp, "model.config.is_valid.password_length.app_error")
   191  		})
   192  
   193  		t.Run("Should not be able to modify PluginSettings.EnableUploads", func(t *testing.T) {
   194  			oldEnableUploads := *th.App.Config().PluginSettings.EnableUploads
   195  			*cfg.PluginSettings.EnableUploads = !oldEnableUploads
   196  
   197  			cfg, resp = client.UpdateConfig(cfg)
   198  			CheckNoError(t, resp)
   199  			assert.Equal(t, oldEnableUploads, *cfg.PluginSettings.EnableUploads)
   200  			assert.Equal(t, oldEnableUploads, *th.App.Config().PluginSettings.EnableUploads)
   201  
   202  			cfg.PluginSettings.EnableUploads = nil
   203  			cfg, resp = client.UpdateConfig(cfg)
   204  			CheckNoError(t, resp)
   205  			assert.Equal(t, oldEnableUploads, *cfg.PluginSettings.EnableUploads)
   206  			assert.Equal(t, oldEnableUploads, *th.App.Config().PluginSettings.EnableUploads)
   207  		})
   208  
   209  		t.Run("Should not be able to modify PluginSettings.SignaturePublicKeyFiles", func(t *testing.T) {
   210  			oldPublicKeys := th.App.Config().PluginSettings.SignaturePublicKeyFiles
   211  			cfg.PluginSettings.SignaturePublicKeyFiles = append(cfg.PluginSettings.SignaturePublicKeyFiles, "new_signature")
   212  
   213  			cfg, resp = client.UpdateConfig(cfg)
   214  			CheckNoError(t, resp)
   215  			assert.Equal(t, oldPublicKeys, cfg.PluginSettings.SignaturePublicKeyFiles)
   216  			assert.Equal(t, oldPublicKeys, th.App.Config().PluginSettings.SignaturePublicKeyFiles)
   217  
   218  			cfg.PluginSettings.SignaturePublicKeyFiles = nil
   219  			cfg, resp = client.UpdateConfig(cfg)
   220  			CheckNoError(t, resp)
   221  			assert.Equal(t, oldPublicKeys, cfg.PluginSettings.SignaturePublicKeyFiles)
   222  			assert.Equal(t, oldPublicKeys, th.App.Config().PluginSettings.SignaturePublicKeyFiles)
   223  		})
   224  	})
   225  
   226  	t.Run("System Admin should not be able to clear Site URL", func(t *testing.T) {
   227  		siteURL := cfg.ServiceSettings.SiteURL
   228  		defer th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.SiteURL = siteURL })
   229  
   230  		nonEmptyURL := "http://localhost"
   231  		cfg.ServiceSettings.SiteURL = &nonEmptyURL
   232  
   233  		// Set the SiteURL
   234  		cfg, resp = th.SystemAdminClient.UpdateConfig(cfg)
   235  		CheckNoError(t, resp)
   236  		require.Equal(t, nonEmptyURL, *cfg.ServiceSettings.SiteURL)
   237  
   238  		// Check that the Site URL can't be cleared
   239  		cfg.ServiceSettings.SiteURL = sToP("")
   240  		cfg, resp = th.SystemAdminClient.UpdateConfig(cfg)
   241  		CheckBadRequestStatus(t, resp)
   242  		CheckErrorMessage(t, resp, "api.config.update_config.clear_siteurl.app_error")
   243  		// Check that the Site URL wasn't cleared
   244  		cfg, resp = th.SystemAdminClient.GetConfig()
   245  		CheckNoError(t, resp)
   246  		require.Equal(t, nonEmptyURL, *cfg.ServiceSettings.SiteURL)
   247  	})
   248  }
   249  
   250  func TestGetConfigWithoutManageSystemPermission(t *testing.T) {
   251  	th := Setup(t)
   252  	defer th.TearDown()
   253  	th.Client.Login(th.BasicUser.Username, th.BasicUser.Password)
   254  
   255  	t.Run("any sysconsole read permission provides config read access", func(t *testing.T) {
   256  		// forbidden by default
   257  		_, resp := th.Client.GetConfig()
   258  		CheckForbiddenStatus(t, resp)
   259  
   260  		// add any sysconsole read permission
   261  		th.AddPermissionToRole(model.SysconsoleReadPermissions[0].Id, model.SYSTEM_USER_ROLE_ID)
   262  		_, resp = th.Client.GetConfig()
   263  
   264  		// should be readable now
   265  		CheckNoError(t, resp)
   266  	})
   267  }
   268  
   269  func TestUpdateConfigWithoutManageSystemPermission(t *testing.T) {
   270  	th := Setup(t)
   271  	defer th.TearDown()
   272  	th.Client.Login(th.BasicUser.Username, th.BasicUser.Password)
   273  
   274  	// add read sysconsole integrations config
   275  	th.AddPermissionToRole(model.PERMISSION_SYSCONSOLE_READ_INTEGRATIONS_INTEGRATION_MANAGEMENT.Id, model.SYSTEM_USER_ROLE_ID)
   276  	defer th.RemovePermissionFromRole(model.PERMISSION_SYSCONSOLE_READ_INTEGRATIONS_INTEGRATION_MANAGEMENT.Id, model.SYSTEM_USER_ROLE_ID)
   277  
   278  	t.Run("sysconsole read permission does not provides config write access", func(t *testing.T) {
   279  		// should be readable because has a sysconsole read permission
   280  		cfg, resp := th.Client.GetConfig()
   281  		CheckNoError(t, resp)
   282  
   283  		_, resp = th.Client.UpdateConfig(cfg)
   284  
   285  		CheckForbiddenStatus(t, resp)
   286  	})
   287  
   288  	t.Run("the wrong write permission does not grant access", func(t *testing.T) {
   289  		// should be readable because has a sysconsole read permission
   290  		cfg, resp := th.SystemAdminClient.GetConfig()
   291  		CheckNoError(t, resp)
   292  
   293  		originalValue := *cfg.ServiceSettings.AllowCorsFrom
   294  
   295  		// add the wrong write permission
   296  		th.AddPermissionToRole(model.PERMISSION_SYSCONSOLE_WRITE_ABOUT_EDITION_AND_LICENSE.Id, model.SYSTEM_USER_ROLE_ID)
   297  		defer th.RemovePermissionFromRole(model.PERMISSION_SYSCONSOLE_WRITE_ABOUT_EDITION_AND_LICENSE.Id, model.SYSTEM_USER_ROLE_ID)
   298  
   299  		// try update a config value allowed by sysconsole WRITE integrations
   300  		mockVal := model.NewId()
   301  		cfg.ServiceSettings.AllowCorsFrom = &mockVal
   302  		_, resp = th.Client.UpdateConfig(cfg)
   303  		CheckNoError(t, resp)
   304  
   305  		// ensure the config setting was not updated
   306  		cfg, resp = th.SystemAdminClient.GetConfig()
   307  		CheckNoError(t, resp)
   308  		assert.Equal(t, *cfg.ServiceSettings.AllowCorsFrom, originalValue)
   309  	})
   310  
   311  	t.Run("config value is writeable by specific system console permission", func(t *testing.T) {
   312  		// should be readable because has a sysconsole read permission
   313  		cfg, resp := th.SystemAdminClient.GetConfig()
   314  		CheckNoError(t, resp)
   315  
   316  		th.AddPermissionToRole(model.PERMISSION_SYSCONSOLE_WRITE_INTEGRATIONS_CORS.Id, model.SYSTEM_USER_ROLE_ID)
   317  		defer th.RemovePermissionFromRole(model.PERMISSION_SYSCONSOLE_WRITE_INTEGRATIONS_CORS.Id, model.SYSTEM_USER_ROLE_ID)
   318  		th.AddPermissionToRole(model.PERMISSION_SYSCONSOLE_READ_INTEGRATIONS_CORS.Id, model.SYSTEM_USER_ROLE_ID)
   319  		defer th.RemovePermissionFromRole(model.PERMISSION_SYSCONSOLE_READ_INTEGRATIONS_CORS.Id, model.SYSTEM_USER_ROLE_ID)
   320  
   321  		// try update a config value allowed by sysconsole WRITE integrations
   322  		mockVal := model.NewId()
   323  		cfg.ServiceSettings.AllowCorsFrom = &mockVal
   324  		_, resp = th.Client.UpdateConfig(cfg)
   325  		CheckNoError(t, resp)
   326  
   327  		// ensure the config setting was updated
   328  		cfg, resp = th.Client.GetConfig()
   329  		CheckNoError(t, resp)
   330  		assert.Equal(t, *cfg.ServiceSettings.AllowCorsFrom, mockVal)
   331  	})
   332  }
   333  
   334  func TestUpdateConfigMessageExportSpecialHandling(t *testing.T) {
   335  	th := Setup(t)
   336  	defer th.TearDown()
   337  
   338  	messageExportEnabled := *th.App.Config().MessageExportSettings.EnableExport
   339  	messageExportTimestamp := *th.App.Config().MessageExportSettings.ExportFromTimestamp
   340  
   341  	defer th.App.UpdateConfig(func(cfg *model.Config) {
   342  		*cfg.MessageExportSettings.EnableExport = messageExportEnabled
   343  		*cfg.MessageExportSettings.ExportFromTimestamp = messageExportTimestamp
   344  	})
   345  
   346  	th.App.UpdateConfig(func(cfg *model.Config) {
   347  		*cfg.MessageExportSettings.EnableExport = false
   348  		*cfg.MessageExportSettings.ExportFromTimestamp = int64(0)
   349  	})
   350  
   351  	// Turn it on, timestamp should be updated.
   352  	cfg, resp := th.SystemAdminClient.GetConfig()
   353  	CheckNoError(t, resp)
   354  
   355  	*cfg.MessageExportSettings.EnableExport = true
   356  	cfg, resp = th.SystemAdminClient.UpdateConfig(cfg)
   357  	CheckNoError(t, resp)
   358  
   359  	assert.True(t, *th.App.Config().MessageExportSettings.EnableExport)
   360  	assert.NotEqual(t, int64(0), *th.App.Config().MessageExportSettings.ExportFromTimestamp)
   361  
   362  	// Turn it off, timestamp should be cleared.
   363  	cfg, resp = th.SystemAdminClient.GetConfig()
   364  	CheckNoError(t, resp)
   365  
   366  	*cfg.MessageExportSettings.EnableExport = false
   367  	cfg, resp = th.SystemAdminClient.UpdateConfig(cfg)
   368  	CheckNoError(t, resp)
   369  
   370  	assert.False(t, *th.App.Config().MessageExportSettings.EnableExport)
   371  	assert.Equal(t, int64(0), *th.App.Config().MessageExportSettings.ExportFromTimestamp)
   372  
   373  	// Set a value from the config file.
   374  	th.App.UpdateConfig(func(cfg *model.Config) {
   375  		*cfg.MessageExportSettings.EnableExport = false
   376  		*cfg.MessageExportSettings.ExportFromTimestamp = int64(12345)
   377  	})
   378  
   379  	// Turn it on, timestamp should *not* be updated.
   380  	cfg, resp = th.SystemAdminClient.GetConfig()
   381  	CheckNoError(t, resp)
   382  
   383  	*cfg.MessageExportSettings.EnableExport = true
   384  	cfg, resp = th.SystemAdminClient.UpdateConfig(cfg)
   385  	CheckNoError(t, resp)
   386  
   387  	assert.True(t, *th.App.Config().MessageExportSettings.EnableExport)
   388  	assert.Equal(t, int64(12345), *th.App.Config().MessageExportSettings.ExportFromTimestamp)
   389  
   390  	// Turn it off, timestamp should be cleared.
   391  	cfg, resp = th.SystemAdminClient.GetConfig()
   392  	CheckNoError(t, resp)
   393  
   394  	*cfg.MessageExportSettings.EnableExport = false
   395  	cfg, resp = th.SystemAdminClient.UpdateConfig(cfg)
   396  	CheckNoError(t, resp)
   397  
   398  	assert.False(t, *th.App.Config().MessageExportSettings.EnableExport)
   399  	assert.Equal(t, int64(0), *th.App.Config().MessageExportSettings.ExportFromTimestamp)
   400  }
   401  
   402  func TestUpdateConfigRestrictSystemAdmin(t *testing.T) {
   403  	th := Setup(t)
   404  	defer th.TearDown()
   405  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ExperimentalSettings.RestrictSystemAdmin = true })
   406  
   407  	t.Run("Restrict flag should be honored for sysadmin", func(t *testing.T) {
   408  		originalCfg, resp := th.SystemAdminClient.GetConfig()
   409  		CheckNoError(t, resp)
   410  
   411  		cfg := originalCfg.Clone()
   412  		*cfg.TeamSettings.SiteName = "MyFancyName"          // Allowed
   413  		*cfg.ServiceSettings.SiteURL = "http://example.com" // Ignored
   414  
   415  		returnedCfg, resp := th.SystemAdminClient.UpdateConfig(cfg)
   416  		CheckNoError(t, resp)
   417  
   418  		require.Equal(t, "MyFancyName", *returnedCfg.TeamSettings.SiteName)
   419  		require.Equal(t, *originalCfg.ServiceSettings.SiteURL, *returnedCfg.ServiceSettings.SiteURL)
   420  
   421  		actualCfg, resp := th.SystemAdminClient.GetConfig()
   422  		CheckNoError(t, resp)
   423  
   424  		require.Equal(t, returnedCfg, actualCfg)
   425  	})
   426  
   427  	t.Run("Restrict flag should be ignored by local mode", func(t *testing.T) {
   428  		originalCfg, resp := th.LocalClient.GetConfig()
   429  		CheckNoError(t, resp)
   430  
   431  		cfg := originalCfg.Clone()
   432  		*cfg.TeamSettings.SiteName = "MyFancyName"          // Allowed
   433  		*cfg.ServiceSettings.SiteURL = "http://example.com" // Ignored
   434  
   435  		returnedCfg, resp := th.LocalClient.UpdateConfig(cfg)
   436  		CheckNoError(t, resp)
   437  
   438  		require.Equal(t, "MyFancyName", *returnedCfg.TeamSettings.SiteName)
   439  		require.Equal(t, "http://example.com", *returnedCfg.ServiceSettings.SiteURL)
   440  	})
   441  }
   442  
   443  func TestUpdateConfigDiffInAuditRecord(t *testing.T) {
   444  	logFile, err := ioutil.TempFile("", "adv.log")
   445  	require.NoError(t, err)
   446  	defer os.Remove(logFile.Name())
   447  
   448  	os.Setenv("MM_EXPERIMENTALAUDITSETTINGS_FILEENABLED", "true")
   449  	os.Setenv("MM_EXPERIMENTALAUDITSETTINGS_FILENAME", logFile.Name())
   450  	defer os.Unsetenv("MM_EXPERIMENTALAUDITSETTINGS_FILEENABLED")
   451  	defer os.Unsetenv("MM_EXPERIMENTALAUDITSETTINGS_FILENAME")
   452  
   453  	options := []app.Option{
   454  		func(s *app.Server) error {
   455  			s.SetLicense(model.NewTestLicense("advanced_logging"))
   456  			return nil
   457  		},
   458  	}
   459  	th := SetupWithServerOptions(t, options)
   460  	defer th.TearDown()
   461  
   462  	cfg, resp := th.SystemAdminClient.GetConfig()
   463  	CheckNoError(t, resp)
   464  
   465  	timeoutVal := *cfg.ServiceSettings.ReadTimeout
   466  	cfg.ServiceSettings.ReadTimeout = model.NewInt(timeoutVal + 1)
   467  	cfg, resp = th.SystemAdminClient.UpdateConfig(cfg)
   468  	CheckNoError(t, resp)
   469  	defer th.App.UpdateConfig(func(cfg *model.Config) {
   470  		cfg.ServiceSettings.ReadTimeout = model.NewInt(timeoutVal)
   471  	})
   472  	require.Equal(t, timeoutVal+1, *cfg.ServiceSettings.ReadTimeout)
   473  	// Forcing a flush before attempting to read log's content.
   474  	err = th.Server.Log.Flush(context.Background())
   475  	require.NoError(t, err)
   476  
   477  	data, err := ioutil.ReadAll(logFile)
   478  	require.NoError(t, err)
   479  	require.NotEmpty(t, data)
   480  	require.Contains(t, string(data),
   481  		fmt.Sprintf(`"diff":"[{Path:ServiceSettings.ReadTimeout BaseVal:%d ActualVal:%d}]"`,
   482  			timeoutVal, timeoutVal+1))
   483  }
   484  
   485  func TestGetEnvironmentConfig(t *testing.T) {
   486  	os.Setenv("MM_SERVICESETTINGS_SITEURL", "http://example.mattermost.com")
   487  	os.Setenv("MM_SERVICESETTINGS_ENABLECUSTOMEMOJI", "true")
   488  	defer os.Unsetenv("MM_SERVICESETTINGS_SITEURL")
   489  	defer os.Unsetenv("MM_SERVICESETTINGS_ENABLECUSTOMEMOJI")
   490  
   491  	th := Setup(t)
   492  	defer th.TearDown()
   493  
   494  	t.Run("as system admin", func(t *testing.T) {
   495  		SystemAdminClient := th.SystemAdminClient
   496  
   497  		envConfig, resp := SystemAdminClient.GetEnvironmentConfig()
   498  		CheckNoError(t, resp)
   499  
   500  		serviceSettings, ok := envConfig["ServiceSettings"]
   501  		require.True(t, ok, "should've returned ServiceSettings")
   502  
   503  		serviceSettingsAsMap, ok := serviceSettings.(map[string]interface{})
   504  		require.True(t, ok, "should've returned ServiceSettings as a map")
   505  
   506  		siteURL, ok := serviceSettingsAsMap["SiteURL"]
   507  		require.True(t, ok, "should've returned ServiceSettings.SiteURL")
   508  
   509  		siteURLAsBool, ok := siteURL.(bool)
   510  		require.True(t, ok, "should've returned ServiceSettings.SiteURL as a boolean")
   511  		require.True(t, siteURLAsBool, "should've returned ServiceSettings.SiteURL as true")
   512  
   513  		enableCustomEmoji, ok := serviceSettingsAsMap["EnableCustomEmoji"]
   514  		require.True(t, ok, "should've returned ServiceSettings.EnableCustomEmoji")
   515  
   516  		enableCustomEmojiAsBool, ok := enableCustomEmoji.(bool)
   517  		require.True(t, ok, "should've returned ServiceSettings.EnableCustomEmoji as a boolean")
   518  		require.True(t, enableCustomEmojiAsBool, "should've returned ServiceSettings.EnableCustomEmoji as true")
   519  
   520  		_, ok = envConfig["TeamSettings"]
   521  		require.False(t, ok, "should not have returned TeamSettings")
   522  	})
   523  
   524  	t.Run("as team admin", func(t *testing.T) {
   525  		TeamAdminClient := th.CreateClient()
   526  		th.LoginTeamAdminWithClient(TeamAdminClient)
   527  
   528  		envConfig, resp := TeamAdminClient.GetEnvironmentConfig()
   529  		CheckNoError(t, resp)
   530  		require.Empty(t, envConfig)
   531  	})
   532  
   533  	t.Run("as regular user", func(t *testing.T) {
   534  		Client := th.Client
   535  
   536  		envConfig, resp := Client.GetEnvironmentConfig()
   537  		CheckNoError(t, resp)
   538  		require.Empty(t, envConfig)
   539  	})
   540  
   541  	t.Run("as not-regular user", func(t *testing.T) {
   542  		Client := th.CreateClient()
   543  
   544  		_, resp := Client.GetEnvironmentConfig()
   545  		CheckUnauthorizedStatus(t, resp)
   546  	})
   547  }
   548  
   549  func TestGetOldClientConfig(t *testing.T) {
   550  	th := Setup(t)
   551  	defer th.TearDown()
   552  
   553  	testKey := "supersecretkey"
   554  	th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.GoogleDeveloperKey = testKey })
   555  
   556  	t.Run("with session", func(t *testing.T) {
   557  		th.App.UpdateConfig(func(cfg *model.Config) {
   558  			*cfg.ServiceSettings.GoogleDeveloperKey = testKey
   559  		})
   560  
   561  		Client := th.Client
   562  
   563  		config, resp := Client.GetOldClientConfig("")
   564  		CheckNoError(t, resp)
   565  
   566  		require.NotEmpty(t, config["Version"], "config not returned correctly")
   567  		require.Equal(t, testKey, config["GoogleDeveloperKey"])
   568  	})
   569  
   570  	t.Run("without session", func(t *testing.T) {
   571  		th.App.UpdateConfig(func(cfg *model.Config) {
   572  			*cfg.ServiceSettings.GoogleDeveloperKey = testKey
   573  		})
   574  
   575  		Client := th.CreateClient()
   576  
   577  		config, resp := Client.GetOldClientConfig("")
   578  		CheckNoError(t, resp)
   579  
   580  		require.NotEmpty(t, config["Version"], "config not returned correctly")
   581  		require.Empty(t, config["GoogleDeveloperKey"], "config should be missing developer key")
   582  	})
   583  
   584  	t.Run("missing format", func(t *testing.T) {
   585  		Client := th.Client
   586  
   587  		_, err := Client.DoApiGet("/config/client", "")
   588  		require.NotNil(t, err)
   589  		require.Equal(t, http.StatusNotImplemented, err.StatusCode)
   590  	})
   591  
   592  	t.Run("invalid format", func(t *testing.T) {
   593  		Client := th.Client
   594  
   595  		_, err := Client.DoApiGet("/config/client?format=junk", "")
   596  		require.NotNil(t, err)
   597  		require.Equal(t, http.StatusBadRequest, err.StatusCode)
   598  	})
   599  }
   600  
   601  func TestPatchConfig(t *testing.T) {
   602  	th := Setup(t)
   603  	defer th.TearDown()
   604  
   605  	t.Run("config is missing", func(t *testing.T) {
   606  		_, response := th.Client.PatchConfig(nil)
   607  		CheckBadRequestStatus(t, response)
   608  	})
   609  
   610  	t.Run("user is not system admin", func(t *testing.T) {
   611  		_, response := th.Client.PatchConfig(&model.Config{})
   612  		CheckForbiddenStatus(t, response)
   613  	})
   614  
   615  	t.Run("should not update the restricted fields when restrict toggle is on for sysadmin", func(t *testing.T) {
   616  		*th.App.Config().ExperimentalSettings.RestrictSystemAdmin = true
   617  
   618  		config := model.Config{LogSettings: model.LogSettings{
   619  			ConsoleLevel: model.NewString("INFO"),
   620  		}}
   621  
   622  		updatedConfig, _ := th.SystemAdminClient.PatchConfig(&config)
   623  
   624  		assert.Equal(t, "DEBUG", *updatedConfig.LogSettings.ConsoleLevel)
   625  	})
   626  
   627  	t.Run("should not bypass the restrict toggle if local client", func(t *testing.T) {
   628  		*th.App.Config().ExperimentalSettings.RestrictSystemAdmin = true
   629  
   630  		config := model.Config{LogSettings: model.LogSettings{
   631  			ConsoleLevel: model.NewString("INFO"),
   632  		}}
   633  
   634  		oldConfig, _ := th.LocalClient.GetConfig()
   635  		updatedConfig, _ := th.LocalClient.PatchConfig(&config)
   636  
   637  		assert.Equal(t, "INFO", *updatedConfig.LogSettings.ConsoleLevel)
   638  		// reset the config
   639  		_, resp := th.LocalClient.UpdateConfig(oldConfig)
   640  		CheckNoError(t, resp)
   641  	})
   642  
   643  	th.TestForSystemAdminAndLocal(t, func(t *testing.T, client *model.Client4) {
   644  		t.Run("check if config is valid", func(t *testing.T) {
   645  			config := model.Config{PasswordSettings: model.PasswordSettings{
   646  				MinimumLength: model.NewInt(4),
   647  			}}
   648  
   649  			_, response := client.PatchConfig(&config)
   650  
   651  			assert.Equal(t, http.StatusBadRequest, response.StatusCode)
   652  			assert.NotNil(t, response.Error)
   653  			assert.Equal(t, "model.config.is_valid.password_length.app_error", response.Error.Id)
   654  		})
   655  
   656  		t.Run("should patch the config", func(t *testing.T) {
   657  			*th.App.Config().ExperimentalSettings.RestrictSystemAdmin = false
   658  			th.App.UpdateConfig(func(cfg *model.Config) { cfg.TeamSettings.ExperimentalDefaultChannels = []string{"some-channel"} })
   659  
   660  			oldConfig, _ := client.GetConfig()
   661  
   662  			assert.False(t, *oldConfig.PasswordSettings.Lowercase)
   663  			assert.NotEqual(t, 15, *oldConfig.PasswordSettings.MinimumLength)
   664  			assert.Equal(t, "DEBUG", *oldConfig.LogSettings.ConsoleLevel)
   665  			assert.True(t, oldConfig.PluginSettings.PluginStates["com.mattermost.nps"].Enable)
   666  
   667  			states := make(map[string]*model.PluginState)
   668  			states["com.mattermost.nps"] = &model.PluginState{Enable: *model.NewBool(false)}
   669  			config := model.Config{PasswordSettings: model.PasswordSettings{
   670  				Lowercase:     model.NewBool(true),
   671  				MinimumLength: model.NewInt(15),
   672  			}, LogSettings: model.LogSettings{
   673  				ConsoleLevel: model.NewString("INFO"),
   674  			},
   675  				TeamSettings: model.TeamSettings{
   676  					ExperimentalDefaultChannels: []string{"another-channel"},
   677  				},
   678  				PluginSettings: model.PluginSettings{
   679  					PluginStates: states,
   680  				},
   681  			}
   682  
   683  			_, response := client.PatchConfig(&config)
   684  
   685  			updatedConfig, _ := client.GetConfig()
   686  			assert.True(t, *updatedConfig.PasswordSettings.Lowercase)
   687  			assert.Equal(t, "INFO", *updatedConfig.LogSettings.ConsoleLevel)
   688  			assert.Equal(t, []string{"another-channel"}, updatedConfig.TeamSettings.ExperimentalDefaultChannels)
   689  			assert.False(t, updatedConfig.PluginSettings.PluginStates["com.mattermost.nps"].Enable)
   690  			assert.Equal(t, "no-cache, no-store, must-revalidate", response.Header.Get("Cache-Control"))
   691  
   692  			// reset the config
   693  			_, resp := client.UpdateConfig(oldConfig)
   694  			CheckNoError(t, resp)
   695  		})
   696  
   697  		t.Run("should sanitize config", func(t *testing.T) {
   698  			config := model.Config{PasswordSettings: model.PasswordSettings{
   699  				Symbol: model.NewBool(true),
   700  			}}
   701  
   702  			updatedConfig, _ := client.PatchConfig(&config)
   703  
   704  			assert.Equal(t, model.FAKE_SETTING, *updatedConfig.SqlSettings.DataSource)
   705  		})
   706  
   707  		t.Run("not allowing to toggle enable uploads for plugin via api", func(t *testing.T) {
   708  			config := model.Config{PluginSettings: model.PluginSettings{
   709  				EnableUploads: model.NewBool(true),
   710  			}}
   711  
   712  			updatedConfig, resp := client.PatchConfig(&config)
   713  			if client == th.LocalClient {
   714  				CheckOKStatus(t, resp)
   715  				assert.Equal(t, true, *updatedConfig.PluginSettings.EnableUploads)
   716  			} else {
   717  				CheckForbiddenStatus(t, resp)
   718  			}
   719  		})
   720  	})
   721  
   722  	t.Run("System Admin should not be able to clear Site URL", func(t *testing.T) {
   723  		cfg, resp := th.SystemAdminClient.GetConfig()
   724  		CheckNoError(t, resp)
   725  		siteURL := cfg.ServiceSettings.SiteURL
   726  		defer th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.SiteURL = siteURL })
   727  
   728  		// Set the SiteURL
   729  		nonEmptyURL := "http://localhost"
   730  		config := model.Config{
   731  			ServiceSettings: model.ServiceSettings{
   732  				SiteURL: model.NewString(nonEmptyURL),
   733  			},
   734  		}
   735  		updatedConfig, resp := th.SystemAdminClient.PatchConfig(&config)
   736  		CheckNoError(t, resp)
   737  		require.Equal(t, nonEmptyURL, *updatedConfig.ServiceSettings.SiteURL)
   738  
   739  		// Check that the Site URL can't be cleared
   740  		config = model.Config{
   741  			ServiceSettings: model.ServiceSettings{
   742  				SiteURL: model.NewString(""),
   743  			},
   744  		}
   745  		updatedConfig, resp = th.SystemAdminClient.PatchConfig(&config)
   746  		CheckBadRequestStatus(t, resp)
   747  		CheckErrorMessage(t, resp, "api.config.update_config.clear_siteurl.app_error")
   748  
   749  		// Check that the Site URL wasn't cleared
   750  		cfg, resp = th.SystemAdminClient.GetConfig()
   751  		CheckNoError(t, resp)
   752  		require.Equal(t, nonEmptyURL, *cfg.ServiceSettings.SiteURL)
   753  
   754  		// Check that sending an empty config returns no error.
   755  		_, resp = th.SystemAdminClient.PatchConfig(&model.Config{})
   756  		CheckNoError(t, resp)
   757  	})
   758  }
   759  
   760  func TestMigrateConfig(t *testing.T) {
   761  	th := Setup(t).InitBasic()
   762  	defer th.TearDown()
   763  
   764  	t.Run("user is not system admin", func(t *testing.T) {
   765  		_, response := th.Client.MigrateConfig("from", "to")
   766  		CheckForbiddenStatus(t, response)
   767  	})
   768  
   769  	th.TestForSystemAdminAndLocal(t, func(t *testing.T, client *model.Client4) {
   770  		f, err := config.NewStoreFromDSN("from.json", false, false, nil)
   771  		require.NoError(t, err)
   772  		defer f.RemoveFile("from.json")
   773  
   774  		_, err = config.NewStoreFromDSN("to.json", false, false, nil)
   775  		require.NoError(t, err)
   776  		defer f.RemoveFile("to.json")
   777  
   778  		_, response := client.MigrateConfig("from.json", "to.json")
   779  		CheckNoError(t, response)
   780  	})
   781  }