mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
* MM-23832: Initial set of changes * MM-23832: further iteration * MM-23832: further iteration * MM-23832: further iteration * MM-23832: Fixes merge. * create migration for new Roles * MM-23832: Renames some roles. * MM-23832: Adds ability to see logs. * MM-23832: Removes manage roles from restricted admin. * MM-23832: Make authentication section read-only for restricted admin. * MM-23832: Allow restricted admin to purge caches. * MM-23832: Adds ability to recycle DB connections. * MM-23832: Adds ability to purge indexes. * MM-23832: Adds ability to test email and S3 config. * MM-23832: Adds abilituy to read job status. * MM-23832: Adds ability to read plugin statuses. * MM-23832: Renames Restricted Admin to System Manager. * MM-23832: Adds manage team roles to system_user_manager. * MM-23832: Updates some permissions. * MM-23832: Allow get all channels and get moderations. * MM-23832: Adds some permissions to User Manager. * MM-23832: Remove write users from user manager. * MM-23832: Changes permissions for the usermanagement > users sysconsole section. * MM-23832: Removes read_settings and write_settings permissions. Ensures the usermanagement parent permissions encompass the sub-permissions. * MM-23832: Updates permissions. * MM-23832: Changes some permissions checks, adds new permissions to roles. * MM-23832: Adds ability to update a role. * MM-23832: Permissions updates. * MM-23832: Removes write access to plugins for system manager. * MM-23832: Removes read compliance from new roles. * MM-23832: Adds mock for new roles creation migration. * MM-23832: Changes to variadic param. * MM-23832: Removes some duplication in the permissions model. Renames some permissions constants. * MM-23832: Updates some migrations. * MM-23832: Removes some unnecessary constants. * MM-23832: Changes back to old app method name. * MM-23832: Fixes incorrect permission check. * MM-23832: Changes write to read permission check. * MM-23832: Removes the authentication permission from link/unlink group. * MM-23832: Enable testing LDAP with read permissions. * MM-23832: Make testing elasticsearch a read permission. * MM-23832: Warn metrics are associated to any system console read permissions. * MM-23832: Updates some permissions checks. * MM-23832: Removes non-systemconsole permissions from roles. * MM-23832: Update default permission assignment of sysadmin. * MM-23832: Fixes incorrect permission check. Removes some unused stuff. * MM-23832: Update permission to check. * MM-23832: Switches to struct tags. * MM-23832: Adds some docs for the permissions tag. * MM-23832: Removes whitespace. * MM-23832: Combines system admin restricted access with other acess-control tag. * MM-23832: Fixes some tests. * MM-23832: Clarifies docs, does not assume prior permission check in '-' access value case. * MM-23832: Updates to correct access tag value. * MM-23832: Adds test of the config settings tag access. * MM-23832: Undoes whitespace change. * MM-23832: Removes comment. * MM-23832: Adds the permissions to the new roles rather than using OR conditions on the permissions checks. * MM-23832: Removes or condition on permission check. * MM-23832: Updates mapping. * MM-23832: Typo fix. * MM-23832: Adds new 'read_jobs' permission. * MM-23832: Add read_jobs to all roles with manage_jobs. * MM-23832: Adds new permission read_other_users_teams. * MM-23832: Adds read filtering of config. * MM-23932: Change tag value. * MM-23832: Fixes some tests. Adds test for read config access tag. * MM-23832: Adds permissions to list teams. * MM-23832: Removes the '-' tag value. Adds a new permission read_channel_groups. Updates a permission check. * MM-23832: Removes unnecessary parent permission for user_management. Fixes permission check change error. * MM-23832: Removes unused parameter to filter/merge function. * MM-23832: Renames migration name. * MM-23832: Fix for godoc. * MM-23832: Fixes tests. * MM-23832: Only makes a map once rather than every function call. Doesn't require access tag on config field structs. Reverts one test update and fixes another. * MM-23832: Removes all of the unnecessary uses of (*App).SessionHasPermissionToAny since removing the user_management parent permission. * MM-23832: Updates constant type. * MM-23832: Removes unnecessary comment. * MM-23832: Renames permissions. * MM-23832: Fix for permission name changes. * MM-23832: Adds missing config access tags. Adds some requirec ancillary permissions for write_usermanagement_teams. * MM-23832: Adds local API endpoint for getting config. * MM-23832: If tag value is blank or restrict_sys_admin_write then don't do the permission check. * MM-23832: nil check for strings prior to dereferencing. * MM-23832: Fix for config display logic. * MM-23832: Updates godoc. * MM-23832: Delays the unrestricted check for parity with other permissions checks if the channel id does not exist. * MM-23832: Removes tautology. * MM-23832: Re-adds status code check. * MM-23832: Adds new permission to edit brand image. * MM-23832: Exports variable for use by mmctl. * MM-23832: Initialize exported map for use by mmctl. * MM-23832: Accept deprecated permissions as valid. * MM-23832: Adds missing permissions to archive a channel. * MM-23832: Adds missing permissions for managing team. * MM-23832: Properly filters config values in patch and update API responses. * MM-23832: Fixes license viewing and writing permissions. * MM-23832: Require license to assign 'new system roles'. * MM-23832: Adds translation keys. * MM-23832: Updates translation order. * MM-27529: Splits read_channel_groups into read_public_channel_groups and read_private_channel_groups. * MM-23832: Prevent read-only permissions from editing site url test parameter. * MM-23832: Prevent read permissions from sniffing ports and elastic password. * MM-23832: Adds missing permission required for write user management channels. * MM-23832: Allows new roles to search for channels. * MM-23832: Adds ability for system_manager to manage jobs. * MM-23832: Cluster status access by sysconsole permission, not manage_system. * MM-23832: Adds 'add_user_to_team' permission to sysconsole write usermanagement teams. * MM-23832: Fixes lint. * MM-23832: Test fix. * MM-23832: Test fix. Co-authored-by: Catalin Tomai <catalin.tomai@mattermost.com> Co-authored-by: Scott Bishel <scott.bishel@mattermost.com> Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
704 lines
24 KiB
Go
704 lines
24 KiB
Go
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
package api4
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/mattermost/mattermost-server/v5/config"
|
|
"github.com/mattermost/mattermost-server/v5/model"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestGetConfig(t *testing.T) {
|
|
th := Setup(t)
|
|
defer th.TearDown()
|
|
Client := th.Client
|
|
|
|
_, resp := Client.GetConfig()
|
|
CheckForbiddenStatus(t, resp)
|
|
|
|
th.TestForSystemAdminAndLocal(t, func(t *testing.T, client *model.Client4) {
|
|
cfg, resp := client.GetConfig()
|
|
CheckNoError(t, resp)
|
|
|
|
require.NotEqual(t, "", cfg.TeamSettings.SiteName)
|
|
|
|
if *cfg.LdapSettings.BindPassword != model.FAKE_SETTING && len(*cfg.LdapSettings.BindPassword) != 0 {
|
|
require.FailNow(t, "did not sanitize properly")
|
|
}
|
|
require.Equal(t, model.FAKE_SETTING, *cfg.FileSettings.PublicLinkSalt, "did not sanitize properly")
|
|
|
|
if *cfg.FileSettings.AmazonS3SecretAccessKey != model.FAKE_SETTING && len(*cfg.FileSettings.AmazonS3SecretAccessKey) != 0 {
|
|
require.FailNow(t, "did not sanitize properly")
|
|
}
|
|
if *cfg.EmailSettings.SMTPPassword != model.FAKE_SETTING && len(*cfg.EmailSettings.SMTPPassword) != 0 {
|
|
require.FailNow(t, "did not sanitize properly")
|
|
}
|
|
if *cfg.GitLabSettings.Secret != model.FAKE_SETTING && len(*cfg.GitLabSettings.Secret) != 0 {
|
|
require.FailNow(t, "did not sanitize properly")
|
|
}
|
|
require.Equal(t, model.FAKE_SETTING, *cfg.SqlSettings.DataSource, "did not sanitize properly")
|
|
require.Equal(t, model.FAKE_SETTING, *cfg.SqlSettings.AtRestEncryptKey, "did not sanitize properly")
|
|
if !strings.Contains(strings.Join(cfg.SqlSettings.DataSourceReplicas, " "), model.FAKE_SETTING) && len(cfg.SqlSettings.DataSourceReplicas) != 0 {
|
|
require.FailNow(t, "did not sanitize properly")
|
|
}
|
|
if !strings.Contains(strings.Join(cfg.SqlSettings.DataSourceSearchReplicas, " "), model.FAKE_SETTING) && len(cfg.SqlSettings.DataSourceSearchReplicas) != 0 {
|
|
require.FailNow(t, "did not sanitize properly")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestGetConfigWithAccessTag(t *testing.T) {
|
|
th := Setup(t)
|
|
defer th.TearDown()
|
|
|
|
varyByHeader := *&th.App.Config().RateLimitSettings.VaryByHeader // environment perm.
|
|
supportEmail := *&th.App.Config().SupportSettings.SupportEmail // site perm.
|
|
defer th.App.UpdateConfig(func(cfg *model.Config) {
|
|
cfg.RateLimitSettings.VaryByHeader = varyByHeader
|
|
cfg.SupportSettings.SupportEmail = supportEmail
|
|
})
|
|
|
|
// set some values so that we know they're not blank
|
|
mockVaryByHeader := model.NewId()
|
|
mockSupportEmail := model.NewId() + "@mattermost.com"
|
|
th.App.UpdateConfig(func(cfg *model.Config) {
|
|
cfg.RateLimitSettings.VaryByHeader = mockVaryByHeader
|
|
cfg.SupportSettings.SupportEmail = &mockSupportEmail
|
|
})
|
|
|
|
th.Client.Login(th.BasicUser.Username, th.BasicUser.Password)
|
|
|
|
// add read sysconsole environment config
|
|
th.AddPermissionToRole(model.PERMISSION_SYSCONSOLE_READ_ENVIRONMENT.Id, model.SYSTEM_USER_ROLE_ID)
|
|
defer th.RemovePermissionFromRole(model.PERMISSION_SYSCONSOLE_READ_ENVIRONMENT.Id, model.SYSTEM_USER_ROLE_ID)
|
|
|
|
cfg, resp := th.Client.GetConfig()
|
|
CheckNoError(t, resp)
|
|
|
|
t.Run("Cannot read value without permission", func(t *testing.T) {
|
|
assert.Nil(t, cfg.SupportSettings.SupportEmail)
|
|
})
|
|
|
|
t.Run("Can read value with permission", func(t *testing.T) {
|
|
assert.Equal(t, mockVaryByHeader, cfg.RateLimitSettings.VaryByHeader)
|
|
})
|
|
}
|
|
|
|
func TestReloadConfig(t *testing.T) {
|
|
th := Setup(t)
|
|
defer th.TearDown()
|
|
Client := th.Client
|
|
|
|
t.Run("as system user", func(t *testing.T) {
|
|
ok, resp := Client.ReloadConfig()
|
|
CheckForbiddenStatus(t, resp)
|
|
require.False(t, ok, "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)
|
|
require.True(t, ok, "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)
|
|
require.False(t, ok, "should not Reload the config due no permission.")
|
|
})
|
|
}
|
|
|
|
func TestUpdateConfig(t *testing.T) {
|
|
th := Setup(t)
|
|
defer th.TearDown()
|
|
Client := th.Client
|
|
|
|
cfg, resp := th.SystemAdminClient.GetConfig()
|
|
CheckNoError(t, resp)
|
|
|
|
_, resp = Client.UpdateConfig(cfg)
|
|
CheckForbiddenStatus(t, resp)
|
|
|
|
th.TestForSystemAdminAndLocal(t, func(t *testing.T, client *model.Client4) {
|
|
SiteName := th.App.Config().TeamSettings.SiteName
|
|
|
|
*cfg.TeamSettings.SiteName = "MyFancyName"
|
|
cfg, resp = client.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 = client.UpdateConfig(cfg)
|
|
CheckNoError(t, resp)
|
|
|
|
require.Equal(t, SiteName, cfg.TeamSettings.SiteName, "It should update the SiteName")
|
|
|
|
t.Run("Should set defaults for missing fields", func(t *testing.T) {
|
|
_, appErr := th.SystemAdminClient.DoApiPut(th.SystemAdminClient.GetConfigRoute(), fmt.Sprintf(`{"ServiceSettings":{"SiteURL":"%s"}}`, *cfg.ServiceSettings.SiteURL))
|
|
require.Nil(t, appErr)
|
|
})
|
|
|
|
t.Run("Should fail with validation error if invalid config setting is passed", func(t *testing.T) {
|
|
//Revert the change
|
|
badcfg := cfg.Clone()
|
|
badcfg.PasswordSettings.MinimumLength = model.NewInt(4)
|
|
badcfg.PasswordSettings.MinimumLength = model.NewInt(4)
|
|
_, resp = client.UpdateConfig(badcfg)
|
|
CheckBadRequestStatus(t, resp)
|
|
CheckErrorMessage(t, resp, "model.config.is_valid.password_length.app_error")
|
|
})
|
|
|
|
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 = client.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 = client.UpdateConfig(cfg)
|
|
CheckNoError(t, resp)
|
|
assert.Equal(t, oldEnableUploads, *cfg.PluginSettings.EnableUploads)
|
|
assert.Equal(t, oldEnableUploads, *th.App.Config().PluginSettings.EnableUploads)
|
|
})
|
|
|
|
t.Run("Should not be able to modify PluginSettings.SignaturePublicKeyFiles", func(t *testing.T) {
|
|
oldPublicKeys := th.App.Config().PluginSettings.SignaturePublicKeyFiles
|
|
cfg.PluginSettings.SignaturePublicKeyFiles = append(cfg.PluginSettings.SignaturePublicKeyFiles, "new_signature")
|
|
|
|
cfg, resp = client.UpdateConfig(cfg)
|
|
CheckNoError(t, resp)
|
|
assert.Equal(t, oldPublicKeys, cfg.PluginSettings.SignaturePublicKeyFiles)
|
|
assert.Equal(t, oldPublicKeys, th.App.Config().PluginSettings.SignaturePublicKeyFiles)
|
|
|
|
cfg.PluginSettings.SignaturePublicKeyFiles = nil
|
|
cfg, resp = client.UpdateConfig(cfg)
|
|
CheckNoError(t, resp)
|
|
assert.Equal(t, oldPublicKeys, cfg.PluginSettings.SignaturePublicKeyFiles)
|
|
assert.Equal(t, oldPublicKeys, th.App.Config().PluginSettings.SignaturePublicKeyFiles)
|
|
})
|
|
})
|
|
|
|
t.Run("System Admin should not be able to clear Site URL", func(t *testing.T) {
|
|
siteURL := cfg.ServiceSettings.SiteURL
|
|
defer th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.SiteURL = siteURL })
|
|
|
|
nonEmptyURL := "http://localhost"
|
|
cfg.ServiceSettings.SiteURL = &nonEmptyURL
|
|
|
|
// Set the SiteURL
|
|
cfg, resp = th.SystemAdminClient.UpdateConfig(cfg)
|
|
CheckNoError(t, resp)
|
|
require.Equal(t, nonEmptyURL, *cfg.ServiceSettings.SiteURL)
|
|
|
|
// Check that the Site URL can't be cleared
|
|
cfg.ServiceSettings.SiteURL = sToP("")
|
|
cfg, resp = th.SystemAdminClient.UpdateConfig(cfg)
|
|
CheckBadRequestStatus(t, resp)
|
|
CheckErrorMessage(t, resp, "api.config.update_config.clear_siteurl.app_error")
|
|
// Check that the Site URL wasn't cleared
|
|
cfg, resp = th.SystemAdminClient.GetConfig()
|
|
CheckNoError(t, resp)
|
|
require.Equal(t, nonEmptyURL, *cfg.ServiceSettings.SiteURL)
|
|
})
|
|
}
|
|
|
|
func TestGetConfigWithoutManageSystemPermission(t *testing.T) {
|
|
th := Setup(t)
|
|
defer th.TearDown()
|
|
th.Client.Login(th.BasicUser.Username, th.BasicUser.Password)
|
|
|
|
t.Run("any sysconsole read permission provides config read access", func(t *testing.T) {
|
|
// forbidden by default
|
|
_, resp := th.Client.GetConfig()
|
|
CheckForbiddenStatus(t, resp)
|
|
|
|
// add any sysconsole read permission
|
|
th.AddPermissionToRole(model.SysconsoleReadPermissions[0].Id, model.SYSTEM_USER_ROLE_ID)
|
|
_, resp = th.Client.GetConfig()
|
|
|
|
// should be readable now
|
|
CheckNoError(t, resp)
|
|
})
|
|
}
|
|
|
|
func TestUpdateConfigWithoutManageSystemPermission(t *testing.T) {
|
|
th := Setup(t)
|
|
defer th.TearDown()
|
|
th.Client.Login(th.BasicUser.Username, th.BasicUser.Password)
|
|
|
|
// add read sysconsole integrations config
|
|
th.AddPermissionToRole(model.PERMISSION_SYSCONSOLE_READ_INTEGRATIONS.Id, model.SYSTEM_USER_ROLE_ID)
|
|
defer th.RemovePermissionFromRole(model.PERMISSION_SYSCONSOLE_READ_INTEGRATIONS.Id, model.SYSTEM_USER_ROLE_ID)
|
|
|
|
t.Run("sysconsole read permission does not provides config write access", func(t *testing.T) {
|
|
// should be readable because has a sysconsole read permission
|
|
cfg, resp := th.Client.GetConfig()
|
|
CheckNoError(t, resp)
|
|
|
|
_, resp = th.Client.UpdateConfig(cfg)
|
|
|
|
CheckForbiddenStatus(t, resp)
|
|
})
|
|
|
|
t.Run("the wrong write permission does not grant access", func(t *testing.T) {
|
|
// should be readable because has a sysconsole read permission
|
|
cfg, resp := th.SystemAdminClient.GetConfig()
|
|
CheckNoError(t, resp)
|
|
|
|
originalValue := *cfg.ServiceSettings.AllowCorsFrom
|
|
|
|
// add the wrong write permission
|
|
th.AddPermissionToRole(model.PERMISSION_SYSCONSOLE_WRITE_ABOUT.Id, model.SYSTEM_USER_ROLE_ID)
|
|
defer th.RemovePermissionFromRole(model.PERMISSION_SYSCONSOLE_WRITE_ABOUT.Id, model.SYSTEM_USER_ROLE_ID)
|
|
|
|
// try update a config value allowed by sysconsole WRITE integrations
|
|
mockVal := model.NewId()
|
|
cfg.ServiceSettings.AllowCorsFrom = &mockVal
|
|
_, resp = th.Client.UpdateConfig(cfg)
|
|
CheckNoError(t, resp)
|
|
|
|
// ensure the config setting was not updated
|
|
cfg, resp = th.Client.GetConfig()
|
|
CheckNoError(t, resp)
|
|
assert.Equal(t, *cfg.ServiceSettings.AllowCorsFrom, originalValue)
|
|
})
|
|
|
|
t.Run("config value is writeable by specific system console permission", func(t *testing.T) {
|
|
// should be readable because has a sysconsole read permission
|
|
cfg, resp := th.SystemAdminClient.GetConfig()
|
|
CheckNoError(t, resp)
|
|
|
|
th.AddPermissionToRole(model.PERMISSION_SYSCONSOLE_WRITE_INTEGRATIONS.Id, model.SYSTEM_USER_ROLE_ID)
|
|
defer th.RemovePermissionFromRole(model.PERMISSION_SYSCONSOLE_WRITE_INTEGRATIONS.Id, model.SYSTEM_USER_ROLE_ID)
|
|
|
|
// try update a config value allowed by sysconsole WRITE integrations
|
|
mockVal := model.NewId()
|
|
cfg.ServiceSettings.AllowCorsFrom = &mockVal
|
|
_, resp = th.Client.UpdateConfig(cfg)
|
|
CheckNoError(t, resp)
|
|
|
|
// ensure the config setting was updated
|
|
cfg, resp = th.Client.GetConfig()
|
|
CheckNoError(t, resp)
|
|
assert.Equal(t, *cfg.ServiceSettings.AllowCorsFrom, mockVal)
|
|
})
|
|
}
|
|
|
|
func TestUpdateConfigMessageExportSpecialHandling(t *testing.T) {
|
|
th := Setup(t)
|
|
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 TestUpdateConfigRestrictSystemAdmin(t *testing.T) {
|
|
th := Setup(t)
|
|
defer th.TearDown()
|
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ExperimentalSettings.RestrictSystemAdmin = true })
|
|
|
|
t.Run("Restrict flag should be honored for sysadmin", func(t *testing.T) {
|
|
originalCfg, resp := th.SystemAdminClient.GetConfig()
|
|
CheckNoError(t, resp)
|
|
|
|
cfg := originalCfg.Clone()
|
|
*cfg.TeamSettings.SiteName = "MyFancyName" // Allowed
|
|
*cfg.ServiceSettings.SiteURL = "http://example.com" // Ignored
|
|
|
|
returnedCfg, resp := th.SystemAdminClient.UpdateConfig(cfg)
|
|
CheckNoError(t, resp)
|
|
|
|
require.Equal(t, "MyFancyName", *returnedCfg.TeamSettings.SiteName)
|
|
require.Equal(t, *originalCfg.ServiceSettings.SiteURL, *returnedCfg.ServiceSettings.SiteURL)
|
|
|
|
actualCfg, resp := th.SystemAdminClient.GetConfig()
|
|
CheckNoError(t, resp)
|
|
|
|
require.Equal(t, returnedCfg, actualCfg)
|
|
})
|
|
|
|
t.Run("Restrict flag should be ignored by local mode", func(t *testing.T) {
|
|
originalCfg, resp := th.LocalClient.GetConfig()
|
|
CheckNoError(t, resp)
|
|
|
|
cfg := originalCfg.Clone()
|
|
*cfg.TeamSettings.SiteName = "MyFancyName" // Allowed
|
|
*cfg.ServiceSettings.SiteURL = "http://example.com" // Ignored
|
|
|
|
returnedCfg, resp := th.LocalClient.UpdateConfig(cfg)
|
|
CheckNoError(t, resp)
|
|
|
|
require.Equal(t, "MyFancyName", *returnedCfg.TeamSettings.SiteName)
|
|
require.Equal(t, "http://example.com", *returnedCfg.ServiceSettings.SiteURL)
|
|
})
|
|
}
|
|
|
|
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")
|
|
defer os.Unsetenv("MM_SERVICESETTINGS_ENABLECUSTOMEMOJI")
|
|
|
|
th := Setup(t)
|
|
defer th.TearDown()
|
|
|
|
t.Run("as system admin", func(t *testing.T) {
|
|
SystemAdminClient := th.SystemAdminClient
|
|
|
|
envConfig, resp := SystemAdminClient.GetEnvironmentConfig()
|
|
CheckNoError(t, resp)
|
|
|
|
serviceSettings, ok := envConfig["ServiceSettings"]
|
|
require.True(t, ok, "should've returned ServiceSettings")
|
|
|
|
serviceSettingsAsMap, ok := serviceSettings.(map[string]interface{})
|
|
require.True(t, ok, "should've returned ServiceSettings as a map")
|
|
|
|
siteURL, ok := serviceSettingsAsMap["SiteURL"]
|
|
require.True(t, ok, "should've returned ServiceSettings.SiteURL")
|
|
|
|
siteURLAsBool, ok := siteURL.(bool)
|
|
require.True(t, ok, "should've returned ServiceSettings.SiteURL as a boolean")
|
|
require.True(t, siteURLAsBool, "should've returned ServiceSettings.SiteURL as true")
|
|
|
|
enableCustomEmoji, ok := serviceSettingsAsMap["EnableCustomEmoji"]
|
|
require.True(t, ok, "should've returned ServiceSettings.EnableCustomEmoji")
|
|
|
|
enableCustomEmojiAsBool, ok := enableCustomEmoji.(bool)
|
|
require.True(t, ok, "should've returned ServiceSettings.EnableCustomEmoji as a boolean")
|
|
require.True(t, enableCustomEmojiAsBool, "should've returned ServiceSettings.EnableCustomEmoji as true")
|
|
|
|
_, ok = envConfig["TeamSettings"]
|
|
require.False(t, ok, "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(t)
|
|
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)
|
|
|
|
require.NotEmpty(t, config["Version"], "config not returned correctly")
|
|
require.Equal(t, testKey, config["GoogleDeveloperKey"])
|
|
})
|
|
|
|
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)
|
|
|
|
require.NotEmpty(t, config["Version"], "config not returned correctly")
|
|
require.Empty(t, config["GoogleDeveloperKey"], "config should be missing developer key")
|
|
})
|
|
|
|
t.Run("missing format", func(t *testing.T) {
|
|
Client := th.Client
|
|
|
|
_, err := Client.DoApiGet("/config/client", "")
|
|
require.NotNil(t, err)
|
|
require.Equal(t, http.StatusNotImplemented, err.StatusCode)
|
|
})
|
|
|
|
t.Run("invalid format", func(t *testing.T) {
|
|
Client := th.Client
|
|
|
|
_, err := Client.DoApiGet("/config/client?format=junk", "")
|
|
require.NotNil(t, err)
|
|
require.Equal(t, http.StatusBadRequest, err.StatusCode)
|
|
})
|
|
}
|
|
|
|
func TestPatchConfig(t *testing.T) {
|
|
th := Setup(t)
|
|
defer th.TearDown()
|
|
|
|
t.Run("config is missing", func(t *testing.T) {
|
|
_, response := th.Client.PatchConfig(nil)
|
|
CheckBadRequestStatus(t, response)
|
|
})
|
|
|
|
t.Run("user is not system admin", func(t *testing.T) {
|
|
_, response := th.Client.PatchConfig(&model.Config{})
|
|
CheckForbiddenStatus(t, response)
|
|
})
|
|
|
|
t.Run("should not update the restricted fields when restrict toggle is on for sysadmin", 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("should not bypass the restrict toggle if local client", func(t *testing.T) {
|
|
*th.App.Config().ExperimentalSettings.RestrictSystemAdmin = true
|
|
|
|
config := model.Config{LogSettings: model.LogSettings{
|
|
ConsoleLevel: model.NewString("INFO"),
|
|
}}
|
|
|
|
oldConfig, _ := th.LocalClient.GetConfig()
|
|
updatedConfig, _ := th.LocalClient.PatchConfig(&config)
|
|
|
|
assert.Equal(t, "INFO", *updatedConfig.LogSettings.ConsoleLevel)
|
|
// reset the config
|
|
_, resp := th.LocalClient.UpdateConfig(oldConfig)
|
|
CheckNoError(t, resp)
|
|
})
|
|
|
|
th.TestForSystemAdminAndLocal(t, func(t *testing.T, client *model.Client4) {
|
|
t.Run("check if config is valid", func(t *testing.T) {
|
|
config := model.Config{PasswordSettings: model.PasswordSettings{
|
|
MinimumLength: model.NewInt(4),
|
|
}}
|
|
|
|
_, response := client.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, _ := client.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 := client.PatchConfig(&config)
|
|
|
|
updatedConfig, _ := client.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"))
|
|
|
|
// reset the config
|
|
_, resp := client.UpdateConfig(oldConfig)
|
|
CheckNoError(t, resp)
|
|
})
|
|
|
|
t.Run("should sanitize config", func(t *testing.T) {
|
|
config := model.Config{PasswordSettings: model.PasswordSettings{
|
|
Symbol: model.NewBool(true),
|
|
}}
|
|
|
|
updatedConfig, _ := client.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, _ := client.PatchConfig(&config)
|
|
|
|
assert.Equal(t, false, *updatedConfig.PluginSettings.EnableUploads)
|
|
})
|
|
})
|
|
|
|
t.Run("System Admin should not be able to clear Site URL", func(t *testing.T) {
|
|
cfg, resp := th.SystemAdminClient.GetConfig()
|
|
CheckNoError(t, resp)
|
|
siteURL := cfg.ServiceSettings.SiteURL
|
|
defer th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.SiteURL = siteURL })
|
|
|
|
// Set the SiteURL
|
|
nonEmptyURL := "http://localhost"
|
|
config := model.Config{
|
|
ServiceSettings: model.ServiceSettings{
|
|
SiteURL: model.NewString(nonEmptyURL),
|
|
},
|
|
}
|
|
updatedConfig, resp := th.SystemAdminClient.PatchConfig(&config)
|
|
CheckNoError(t, resp)
|
|
require.Equal(t, nonEmptyURL, *updatedConfig.ServiceSettings.SiteURL)
|
|
|
|
// Check that the Site URL can't be cleared
|
|
config = model.Config{
|
|
ServiceSettings: model.ServiceSettings{
|
|
SiteURL: model.NewString(""),
|
|
},
|
|
}
|
|
updatedConfig, resp = th.SystemAdminClient.PatchConfig(&config)
|
|
CheckBadRequestStatus(t, resp)
|
|
CheckErrorMessage(t, resp, "api.config.update_config.clear_siteurl.app_error")
|
|
|
|
// Check that the Site URL wasn't cleared
|
|
cfg, resp = th.SystemAdminClient.GetConfig()
|
|
CheckNoError(t, resp)
|
|
require.Equal(t, nonEmptyURL, *cfg.ServiceSettings.SiteURL)
|
|
|
|
// Check that sending an empty config returns an error.
|
|
_, resp = th.SystemAdminClient.PatchConfig(&model.Config{})
|
|
require.NotNil(t, resp.Error)
|
|
CheckBadRequestStatus(t, resp)
|
|
assert.Equal(t, "api.config.update_config.clear_siteurl.app_error", resp.Error.Id)
|
|
})
|
|
}
|
|
|
|
func TestMigrateConfig(t *testing.T) {
|
|
th := Setup(t).InitBasic()
|
|
defer th.TearDown()
|
|
|
|
t.Run("user is not system admin", func(t *testing.T) {
|
|
_, response := th.Client.MigrateConfig("from", "to")
|
|
CheckForbiddenStatus(t, response)
|
|
})
|
|
|
|
th.TestForSystemAdminAndLocal(t, func(t *testing.T, client *model.Client4) {
|
|
f, err := config.NewStore("from.json", false)
|
|
require.NoError(t, err)
|
|
defer f.RemoveFile("from.json")
|
|
|
|
_, err = config.NewStore("to.json", false)
|
|
require.NoError(t, err)
|
|
defer f.RemoveFile("to.json")
|
|
|
|
_, response := client.MigrateConfig("from.json", "to.json")
|
|
CheckNoError(t, response)
|
|
})
|
|
}
|