snapshot of https://github.com/mattermost/mattermost.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 2s
Server CI Nightly Race / Build Environment (push) Failing after 1s
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 2s
Server CI Nightly Race / Build Environment (push) Failing after 1s
This commit is contained in:
351
server/channels/app/config.go
Normal file
351
server/channels/app/config.go
Normal file
@@ -0,0 +1,351 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/rand"
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/mattermost/mattermost/server/public/model"
|
||||
"github.com/mattermost/mattermost/server/public/shared/mlog"
|
||||
"github.com/mattermost/mattermost/server/v8/channels/utils"
|
||||
"github.com/mattermost/mattermost/server/v8/platform/shared/mail"
|
||||
)
|
||||
|
||||
const (
|
||||
ErrorTermsOfServiceNoRowsFound = "app.terms_of_service.get.no_rows.app_error"
|
||||
)
|
||||
|
||||
func (s *Server) Config() *model.Config {
|
||||
return s.platform.Config()
|
||||
}
|
||||
|
||||
func (a *App) Config() *model.Config {
|
||||
return a.ch.cfgSvc.Config()
|
||||
}
|
||||
|
||||
func (a *App) EnvironmentConfig(filter func(reflect.StructField) bool) map[string]any {
|
||||
return a.Srv().platform.GetEnvironmentOverridesWithFilter(filter)
|
||||
}
|
||||
|
||||
func (a *App) UpdateConfig(f func(*model.Config)) {
|
||||
a.Srv().platform.UpdateConfig(f)
|
||||
}
|
||||
|
||||
func (a *App) IsConfigReadOnly() bool {
|
||||
return a.Srv().platform.IsConfigReadOnly()
|
||||
}
|
||||
|
||||
func (a *App) ReloadConfig() error {
|
||||
return a.Srv().platform.ReloadConfig()
|
||||
}
|
||||
|
||||
func (a *App) ClientConfig() map[string]string {
|
||||
return a.ch.srv.platform.ClientConfig()
|
||||
}
|
||||
|
||||
func (a *App) ClientConfigHash() string {
|
||||
return a.ch.ClientConfigHash()
|
||||
}
|
||||
|
||||
func (a *App) LimitedClientConfig() map[string]string {
|
||||
return a.ch.srv.platform.LimitedClientConfig()
|
||||
}
|
||||
|
||||
func (a *App) AddConfigListener(listener func(*model.Config, *model.Config)) string {
|
||||
return a.Srv().platform.AddConfigListener(listener)
|
||||
}
|
||||
|
||||
func (a *App) AddLicenseListener(listener func(oldLicense, newLicense *model.License)) string {
|
||||
return a.Srv().platform.AddLicenseListener(listener)
|
||||
}
|
||||
|
||||
// Removes a listener function by the unique ID returned when AddConfigListener was called
|
||||
func (a *App) RemoveConfigListener(id string) {
|
||||
a.Srv().platform.RemoveConfigListener(id)
|
||||
}
|
||||
|
||||
// ensurePostActionCookieSecret ensures that the key for encrypting PostActionCookie exists
|
||||
// and future calls to PostActionCookieSecret will always return a valid key, same on all
|
||||
// servers in the cluster
|
||||
func (ch *Channels) ensurePostActionCookieSecret() error {
|
||||
if ch.postActionCookieSecret != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var secret *model.SystemPostActionCookieSecret
|
||||
|
||||
value, err := ch.srv.Store().System().GetByName(model.SystemPostActionCookieSecretKey)
|
||||
if err == nil {
|
||||
if err := json.Unmarshal([]byte(value.Value), &secret); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// If we don't already have a key, try to generate one.
|
||||
if secret == nil {
|
||||
newSecret := &model.SystemPostActionCookieSecret{
|
||||
Secret: make([]byte, 32),
|
||||
}
|
||||
_, err := rand.Reader.Read(newSecret.Secret)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
system := &model.System{
|
||||
Name: model.SystemPostActionCookieSecretKey,
|
||||
}
|
||||
v, err := json.Marshal(newSecret)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
system.Value = string(v)
|
||||
// If we were able to save the key, use it, otherwise log the error.
|
||||
if err = ch.srv.Store().System().Save(system); err != nil {
|
||||
mlog.Warn("Failed to save PostActionCookieSecret", mlog.Err(err))
|
||||
} else {
|
||||
secret = newSecret
|
||||
}
|
||||
}
|
||||
|
||||
// If we weren't able to save a new key above, another server must have beat us to it. Get the
|
||||
// key from the database, and if that fails, error out.
|
||||
if secret == nil {
|
||||
value, err := ch.srv.Store().System().GetByName(model.SystemPostActionCookieSecretKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := json.Unmarshal([]byte(value.Value), &secret); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
ch.postActionCookieSecret = secret.Secret
|
||||
return nil
|
||||
}
|
||||
|
||||
// ensureSamlRelayStateSigningKey ensures that the key for signing SAML RelayState exists
|
||||
// and future calls to SamlRelayStateSigningKey will always return a valid key, same on all
|
||||
// servers in the cluster
|
||||
func (ch *Channels) ensureSamlRelayStateSigningKey() error {
|
||||
if ch.samlRelayStateSigningKey != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var secret *model.SystemSamlRelayStateSigningKey
|
||||
|
||||
value, err := ch.srv.Store().System().GetByName(model.SystemSamlRelayStateSigningKeyKey)
|
||||
if err == nil {
|
||||
if err := json.Unmarshal([]byte(value.Value), &secret); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// If we don't already have a key, try to generate one.
|
||||
if secret == nil {
|
||||
newSecret := &model.SystemSamlRelayStateSigningKey{
|
||||
Secret: make([]byte, 32),
|
||||
}
|
||||
_, err := rand.Reader.Read(newSecret.Secret)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
system := &model.System{
|
||||
Name: model.SystemSamlRelayStateSigningKeyKey,
|
||||
}
|
||||
v, err := json.Marshal(newSecret)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
system.Value = string(v)
|
||||
// If we were able to save the key, use it, otherwise log the error.
|
||||
if err = ch.srv.Store().System().Save(system); err != nil {
|
||||
mlog.Warn("Failed to save SamlRelayStateSigningKey", mlog.Err(err))
|
||||
} else {
|
||||
secret = newSecret
|
||||
}
|
||||
}
|
||||
|
||||
// If we weren't able to save a new key above, another server must have beat us to it. Get the
|
||||
// key from the database, and if that fails, error out.
|
||||
if secret == nil {
|
||||
value, err := ch.srv.Store().System().GetByName(model.SystemSamlRelayStateSigningKeyKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := json.Unmarshal([]byte(value.Value), &secret); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
ch.samlRelayStateSigningKey = secret.Secret
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) ensureInstallationDate() error {
|
||||
_, appErr := s.platform.GetSystemInstallDate()
|
||||
if appErr == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
installDate, nErr := s.Store().User().InferSystemInstallDate()
|
||||
var installationDate int64
|
||||
if nErr == nil && installDate > 0 {
|
||||
installationDate = installDate
|
||||
} else {
|
||||
installationDate = utils.MillisFromTime(time.Now())
|
||||
}
|
||||
|
||||
if err := s.Store().System().SaveOrUpdate(&model.System{
|
||||
Name: model.SystemInstallationDateKey,
|
||||
Value: strconv.FormatInt(installationDate, 10),
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) ensureFirstServerRunTimestamp() error {
|
||||
_, appErr := s.getFirstServerRunTimestamp()
|
||||
if appErr == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := s.Store().System().SaveOrUpdate(&model.System{
|
||||
Name: model.SystemFirstServerRunTimestampKey,
|
||||
Value: strconv.FormatInt(utils.MillisFromTime(time.Now()), 10),
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AsymmetricSigningKey will return a private key that can be used for asymmetric signing.
|
||||
func (ch *Channels) AsymmetricSigningKey() *ecdsa.PrivateKey {
|
||||
return ch.srv.platform.AsymmetricSigningKey()
|
||||
}
|
||||
|
||||
func (a *App) AsymmetricSigningKey() *ecdsa.PrivateKey {
|
||||
return a.ch.AsymmetricSigningKey()
|
||||
}
|
||||
|
||||
func (ch *Channels) PostActionCookieSecret() []byte {
|
||||
return ch.postActionCookieSecret
|
||||
}
|
||||
|
||||
func (a *App) PostActionCookieSecret() []byte {
|
||||
return a.ch.PostActionCookieSecret()
|
||||
}
|
||||
|
||||
func (ch *Channels) SamlRelayStateSigningKey() []byte {
|
||||
return ch.samlRelayStateSigningKey
|
||||
}
|
||||
|
||||
func (a *App) SamlRelayStateSigningKey() []byte {
|
||||
return a.ch.SamlRelayStateSigningKey()
|
||||
}
|
||||
|
||||
func (a *App) GetCookieDomain() string {
|
||||
if *a.Config().ServiceSettings.AllowCookiesForSubdomains {
|
||||
if siteURL, err := url.Parse(*a.Config().ServiceSettings.SiteURL); err == nil {
|
||||
return siteURL.Hostname()
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (a *App) GetSiteURL() string {
|
||||
return *a.Config().ServiceSettings.SiteURL
|
||||
}
|
||||
|
||||
// GetConfigFile proxies access to the given configuration file to the underlying config store.
|
||||
func (a *App) GetConfigFile(name string) ([]byte, error) {
|
||||
data, err := a.Srv().platform.GetConfigFile(name)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get config file %s", name)
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// GetSanitizedConfig gets the configuration for a system admin without any secrets.
|
||||
func (a *App) GetSanitizedConfig() *model.Config {
|
||||
cfg := a.Config().Clone()
|
||||
|
||||
a.SanitizedConfig(cfg)
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
// SanitizedConfig sanitizes a given configuration for a system admin without any secrets.
|
||||
func (a *App) SanitizedConfig(cfg *model.Config) {
|
||||
manifests, err := a.getPluginManifests()
|
||||
if err != nil {
|
||||
// GetPluginManifests might error, e.g. when plugins are disabled.
|
||||
// Sanitize all plugin settings in this case.
|
||||
cfg.Sanitize(nil, nil)
|
||||
return
|
||||
}
|
||||
|
||||
cfg.Sanitize(manifests, nil)
|
||||
}
|
||||
|
||||
// GetEnvironmentConfig returns a map of configuration keys whose values have been overridden by an environment variable.
|
||||
// If filter is not nil and returns false for a struct field, that field will be omitted.
|
||||
func (a *App) GetEnvironmentConfig(filter func(reflect.StructField) bool) map[string]any {
|
||||
return a.EnvironmentConfig(filter)
|
||||
}
|
||||
|
||||
// SaveConfig replaces the active configuration, optionally notifying cluster peers.
|
||||
func (a *App) SaveConfig(newCfg *model.Config, sendConfigChangeClusterMessage bool) (*model.Config, *model.Config, *model.AppError) {
|
||||
return a.Srv().platform.SaveConfig(newCfg, sendConfigChangeClusterMessage)
|
||||
}
|
||||
|
||||
func (a *App) HandleMessageExportConfig(cfg *model.Config, appCfg *model.Config) {
|
||||
// If the Message Export feature has been toggled in the System Console, rewrite the ExportFromTimestamp field to an
|
||||
// appropriate value. The rewriting occurs here to ensure it doesn't affect values written to the config file
|
||||
// directly and not through the System Console UI.
|
||||
if *cfg.MessageExportSettings.EnableExport != *appCfg.MessageExportSettings.EnableExport {
|
||||
if *cfg.MessageExportSettings.EnableExport && model.SafeDereference(cfg.MessageExportSettings.ExportFromTimestamp) == int64(0) {
|
||||
// When the feature is toggled on, use the current timestamp as the start time for future exports.
|
||||
cfg.MessageExportSettings.ExportFromTimestamp = new(model.GetMillis())
|
||||
} else if !*cfg.MessageExportSettings.EnableExport {
|
||||
// When the feature is disabled, reset the timestamp so that the timestamp will be set if
|
||||
// the feature is re-enabled from the System Console in future.
|
||||
cfg.MessageExportSettings.ExportFromTimestamp = new(int64(0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) MailServiceConfig() *mail.SMTPConfig {
|
||||
emailSettings := s.platform.Config().EmailSettings
|
||||
hostname := utils.GetHostnameFromSiteURL(*s.platform.Config().ServiceSettings.SiteURL)
|
||||
cfg := mail.SMTPConfig{
|
||||
Hostname: hostname,
|
||||
ConnectionSecurity: *emailSettings.ConnectionSecurity,
|
||||
SkipServerCertificateVerification: *emailSettings.SkipServerCertificateVerification,
|
||||
ServerName: *emailSettings.SMTPServer,
|
||||
Server: *emailSettings.SMTPServer,
|
||||
Port: *emailSettings.SMTPPort,
|
||||
ServerTimeout: *emailSettings.SMTPServerTimeout,
|
||||
Username: *emailSettings.SMTPUsername,
|
||||
Password: *emailSettings.SMTPPassword,
|
||||
EnableSMTPAuth: *emailSettings.EnableSMTPAuth,
|
||||
SendEmailNotifications: *emailSettings.SendEmailNotifications,
|
||||
FeedbackName: *emailSettings.FeedbackName,
|
||||
FeedbackEmail: *emailSettings.FeedbackEmail,
|
||||
ReplyToAddress: *emailSettings.ReplyToAddress,
|
||||
}
|
||||
return &cfg
|
||||
}
|
||||
Reference in New Issue
Block a user