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 }