github.com/mattermosttest/mattermost-server/v5@v5.0.0-20200917143240-9dfa12e121f9/app/config.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See LICENSE.txt for license information.
     3  
     4  package app
     5  
     6  import (
     7  	"crypto/ecdsa"
     8  	"crypto/elliptic"
     9  	"crypto/md5"
    10  	"crypto/rand"
    11  	"crypto/x509"
    12  	"encoding/base64"
    13  	"encoding/json"
    14  	"fmt"
    15  	"net/http"
    16  	"net/url"
    17  	"runtime/debug"
    18  	"strconv"
    19  	"time"
    20  
    21  	"github.com/pkg/errors"
    22  
    23  	"github.com/mattermost/mattermost-server/v5/config"
    24  	"github.com/mattermost/mattermost-server/v5/mlog"
    25  	"github.com/mattermost/mattermost-server/v5/model"
    26  	"github.com/mattermost/mattermost-server/v5/utils"
    27  )
    28  
    29  const (
    30  	ERROR_TERMS_OF_SERVICE_NO_ROWS_FOUND = "app.terms_of_service.get.no_rows.app_error"
    31  )
    32  
    33  func (s *Server) Config() *model.Config {
    34  	return s.configStore.Get()
    35  }
    36  
    37  func (a *App) Config() *model.Config {
    38  	return a.Srv().Config()
    39  }
    40  
    41  func (s *Server) EnvironmentConfig() map[string]interface{} {
    42  	return s.configStore.GetEnvironmentOverrides()
    43  }
    44  
    45  func (a *App) EnvironmentConfig() map[string]interface{} {
    46  	return a.Srv().EnvironmentConfig()
    47  }
    48  
    49  func (s *Server) UpdateConfig(f func(*model.Config)) {
    50  	old := s.Config()
    51  	updated := old.Clone()
    52  	f(updated)
    53  	if _, err := s.configStore.Set(updated); err != nil {
    54  		mlog.Error("Failed to update config", mlog.Err(err))
    55  	}
    56  }
    57  
    58  func (a *App) UpdateConfig(f func(*model.Config)) {
    59  	a.Srv().UpdateConfig(f)
    60  }
    61  
    62  func (s *Server) ReloadConfig() error {
    63  	debug.FreeOSMemory()
    64  	if err := s.configStore.Load(); err != nil {
    65  		return err
    66  	}
    67  	return nil
    68  }
    69  
    70  func (a *App) ReloadConfig() error {
    71  	return a.Srv().ReloadConfig()
    72  }
    73  
    74  func (a *App) ClientConfig() map[string]string {
    75  	return a.Srv().clientConfig.Load().(map[string]string)
    76  }
    77  
    78  func (a *App) ClientConfigHash() string {
    79  	return a.Srv().ClientConfigHash()
    80  }
    81  
    82  func (a *App) LimitedClientConfig() map[string]string {
    83  	return a.Srv().limitedClientConfig.Load().(map[string]string)
    84  }
    85  
    86  // Registers a function with a given listener to be called when the config is reloaded and may have changed. The function
    87  // will be called with two arguments: the old config and the new config. AddConfigListener returns a unique ID
    88  // for the listener that can later be used to remove it.
    89  func (s *Server) AddConfigListener(listener func(*model.Config, *model.Config)) string {
    90  	return s.configStore.AddListener(listener)
    91  }
    92  
    93  func (a *App) AddConfigListener(listener func(*model.Config, *model.Config)) string {
    94  	return a.Srv().AddConfigListener(listener)
    95  }
    96  
    97  // Removes a listener function by the unique ID returned when AddConfigListener was called
    98  func (s *Server) RemoveConfigListener(id string) {
    99  	s.configStore.RemoveListener(id)
   100  }
   101  
   102  func (a *App) RemoveConfigListener(id string) {
   103  	a.Srv().RemoveConfigListener(id)
   104  }
   105  
   106  // ensurePostActionCookieSecret ensures that the key for encrypting PostActionCookie exists
   107  // and future calls to PostActionCookieSecret will always return a valid key, same on all
   108  // servers in the cluster
   109  func (s *Server) ensurePostActionCookieSecret() error {
   110  	if s.postActionCookieSecret != nil {
   111  		return nil
   112  	}
   113  
   114  	var secret *model.SystemPostActionCookieSecret
   115  
   116  	value, err := s.Store.System().GetByName(model.SYSTEM_POST_ACTION_COOKIE_SECRET)
   117  	if err == nil {
   118  		if err := json.Unmarshal([]byte(value.Value), &secret); err != nil {
   119  			return err
   120  		}
   121  	}
   122  
   123  	// If we don't already have a key, try to generate one.
   124  	if secret == nil {
   125  		newSecret := &model.SystemPostActionCookieSecret{
   126  			Secret: make([]byte, 32),
   127  		}
   128  		_, err := rand.Reader.Read(newSecret.Secret)
   129  		if err != nil {
   130  			return err
   131  		}
   132  
   133  		system := &model.System{
   134  			Name: model.SYSTEM_POST_ACTION_COOKIE_SECRET,
   135  		}
   136  		v, err := json.Marshal(newSecret)
   137  		if err != nil {
   138  			return err
   139  		}
   140  		system.Value = string(v)
   141  		// If we were able to save the key, use it, otherwise log the error.
   142  		if appErr := s.Store.System().Save(system); appErr != nil {
   143  			mlog.Error("Failed to save PostActionCookieSecret", mlog.Err(appErr))
   144  		} else {
   145  			secret = newSecret
   146  		}
   147  	}
   148  
   149  	// If we weren't able to save a new key above, another server must have beat us to it. Get the
   150  	// key from the database, and if that fails, error out.
   151  	if secret == nil {
   152  		value, err := s.Store.System().GetByName(model.SYSTEM_POST_ACTION_COOKIE_SECRET)
   153  		if err != nil {
   154  			return err
   155  		}
   156  
   157  		if err := json.Unmarshal([]byte(value.Value), &secret); err != nil {
   158  			return err
   159  		}
   160  	}
   161  
   162  	s.postActionCookieSecret = secret.Secret
   163  	return nil
   164  }
   165  
   166  // ensureAsymmetricSigningKey ensures that an asymmetric signing key exists and future calls to
   167  // AsymmetricSigningKey will always return a valid signing key.
   168  func (s *Server) ensureAsymmetricSigningKey() error {
   169  	if s.asymmetricSigningKey != nil {
   170  		return nil
   171  	}
   172  
   173  	var key *model.SystemAsymmetricSigningKey
   174  
   175  	value, err := s.Store.System().GetByName(model.SYSTEM_ASYMMETRIC_SIGNING_KEY)
   176  	if err == nil {
   177  		if err := json.Unmarshal([]byte(value.Value), &key); err != nil {
   178  			return err
   179  		}
   180  	}
   181  
   182  	// If we don't already have a key, try to generate one.
   183  	if key == nil {
   184  		newECDSAKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
   185  		if err != nil {
   186  			return err
   187  		}
   188  		newKey := &model.SystemAsymmetricSigningKey{
   189  			ECDSAKey: &model.SystemECDSAKey{
   190  				Curve: "P-256",
   191  				X:     newECDSAKey.X,
   192  				Y:     newECDSAKey.Y,
   193  				D:     newECDSAKey.D,
   194  			},
   195  		}
   196  		system := &model.System{
   197  			Name: model.SYSTEM_ASYMMETRIC_SIGNING_KEY,
   198  		}
   199  		v, err := json.Marshal(newKey)
   200  		if err != nil {
   201  			return err
   202  		}
   203  		system.Value = string(v)
   204  		// If we were able to save the key, use it, otherwise log the error.
   205  		if appErr := s.Store.System().Save(system); appErr != nil {
   206  			mlog.Error("Failed to save AsymmetricSigningKey", mlog.Err(appErr))
   207  		} else {
   208  			key = newKey
   209  		}
   210  	}
   211  
   212  	// If we weren't able to save a new key above, another server must have beat us to it. Get the
   213  	// key from the database, and if that fails, error out.
   214  	if key == nil {
   215  		value, err := s.Store.System().GetByName(model.SYSTEM_ASYMMETRIC_SIGNING_KEY)
   216  		if err != nil {
   217  			return err
   218  		}
   219  
   220  		if err := json.Unmarshal([]byte(value.Value), &key); err != nil {
   221  			return err
   222  		}
   223  	}
   224  
   225  	var curve elliptic.Curve
   226  	switch key.ECDSAKey.Curve {
   227  	case "P-256":
   228  		curve = elliptic.P256()
   229  	default:
   230  		return fmt.Errorf("unknown curve: " + key.ECDSAKey.Curve)
   231  	}
   232  	s.asymmetricSigningKey = &ecdsa.PrivateKey{
   233  		PublicKey: ecdsa.PublicKey{
   234  			Curve: curve,
   235  			X:     key.ECDSAKey.X,
   236  			Y:     key.ECDSAKey.Y,
   237  		},
   238  		D: key.ECDSAKey.D,
   239  	}
   240  	s.regenerateClientConfig()
   241  	return nil
   242  }
   243  
   244  func (s *Server) ensureInstallationDate() error {
   245  	_, err := s.getSystemInstallDate()
   246  	if err == nil {
   247  		return nil
   248  	}
   249  
   250  	installDate, err := s.Store.User().InferSystemInstallDate()
   251  	var installationDate int64
   252  	if err == nil && installDate > 0 {
   253  		installationDate = installDate
   254  	} else {
   255  		installationDate = utils.MillisFromTime(time.Now())
   256  	}
   257  
   258  	err = s.Store.System().SaveOrUpdate(&model.System{
   259  		Name:  model.SYSTEM_INSTALLATION_DATE_KEY,
   260  		Value: strconv.FormatInt(installationDate, 10),
   261  	})
   262  	if err != nil {
   263  		return err
   264  	}
   265  	return nil
   266  }
   267  
   268  func (s *Server) ensureFirstServerRunTimestamp() error {
   269  	_, err := s.getFirstServerRunTimestamp()
   270  	if err == nil {
   271  		return nil
   272  	}
   273  
   274  	err = s.Store.System().SaveOrUpdate(&model.System{
   275  		Name:  model.SYSTEM_FIRST_SERVER_RUN_TIMESTAMP_KEY,
   276  		Value: strconv.FormatInt(utils.MillisFromTime(time.Now()), 10),
   277  	})
   278  	if err != nil {
   279  		return err
   280  	}
   281  	return nil
   282  }
   283  
   284  // AsymmetricSigningKey will return a private key that can be used for asymmetric signing.
   285  func (s *Server) AsymmetricSigningKey() *ecdsa.PrivateKey {
   286  	return s.asymmetricSigningKey
   287  }
   288  
   289  func (a *App) AsymmetricSigningKey() *ecdsa.PrivateKey {
   290  	return a.Srv().AsymmetricSigningKey()
   291  }
   292  
   293  func (s *Server) PostActionCookieSecret() []byte {
   294  	return s.postActionCookieSecret
   295  }
   296  
   297  func (a *App) PostActionCookieSecret() []byte {
   298  	return a.Srv().PostActionCookieSecret()
   299  }
   300  
   301  func (s *Server) regenerateClientConfig() {
   302  	clientConfig := config.GenerateClientConfig(s.Config(), s.diagnosticId, s.License())
   303  	limitedClientConfig := config.GenerateLimitedClientConfig(s.Config(), s.diagnosticId, s.License())
   304  
   305  	if clientConfig["EnableCustomTermsOfService"] == "true" {
   306  		termsOfService, err := s.Store.TermsOfService().GetLatest(true)
   307  		if err != nil {
   308  			mlog.Err(err)
   309  		} else {
   310  			clientConfig["CustomTermsOfServiceId"] = termsOfService.Id
   311  			limitedClientConfig["CustomTermsOfServiceId"] = termsOfService.Id
   312  		}
   313  	}
   314  
   315  	if key := s.AsymmetricSigningKey(); key != nil {
   316  		der, _ := x509.MarshalPKIXPublicKey(&key.PublicKey)
   317  		clientConfig["AsymmetricSigningPublicKey"] = base64.StdEncoding.EncodeToString(der)
   318  		limitedClientConfig["AsymmetricSigningPublicKey"] = base64.StdEncoding.EncodeToString(der)
   319  	}
   320  
   321  	clientConfigJSON, _ := json.Marshal(clientConfig)
   322  	s.clientConfig.Store(clientConfig)
   323  	s.limitedClientConfig.Store(limitedClientConfig)
   324  	s.clientConfigHash.Store(fmt.Sprintf("%x", md5.Sum(clientConfigJSON)))
   325  }
   326  
   327  func (a *App) GetCookieDomain() string {
   328  	if *a.Config().ServiceSettings.AllowCookiesForSubdomains {
   329  		if siteURL, err := url.Parse(*a.Config().ServiceSettings.SiteURL); err == nil {
   330  			return siteURL.Hostname()
   331  		}
   332  	}
   333  	return ""
   334  }
   335  
   336  func (a *App) GetSiteURL() string {
   337  	return *a.Config().ServiceSettings.SiteURL
   338  }
   339  
   340  // ClientConfigWithComputed gets the configuration in a format suitable for sending to the client.
   341  func (s *Server) ClientConfigWithComputed() map[string]string {
   342  	respCfg := map[string]string{}
   343  	for k, v := range s.clientConfig.Load().(map[string]string) {
   344  		respCfg[k] = v
   345  	}
   346  
   347  	// These properties are not configurable, but nevertheless represent configuration expected
   348  	// by the client.
   349  	respCfg["NoAccounts"] = strconv.FormatBool(s.IsFirstUserAccount())
   350  	respCfg["MaxPostSize"] = strconv.Itoa(s.MaxPostSize())
   351  	respCfg["InstallationDate"] = ""
   352  	if installationDate, err := s.getSystemInstallDate(); err == nil {
   353  		respCfg["InstallationDate"] = strconv.FormatInt(installationDate, 10)
   354  	}
   355  
   356  	return respCfg
   357  }
   358  
   359  // ClientConfigWithComputed gets the configuration in a format suitable for sending to the client.
   360  func (a *App) ClientConfigWithComputed() map[string]string {
   361  	return a.Srv().ClientConfigWithComputed()
   362  }
   363  
   364  // LimitedClientConfigWithComputed gets the configuration in a format suitable for sending to the client.
   365  func (a *App) LimitedClientConfigWithComputed() map[string]string {
   366  	respCfg := map[string]string{}
   367  	for k, v := range a.LimitedClientConfig() {
   368  		respCfg[k] = v
   369  	}
   370  
   371  	// These properties are not configurable, but nevertheless represent configuration expected
   372  	// by the client.
   373  	respCfg["NoAccounts"] = strconv.FormatBool(a.IsFirstUserAccount())
   374  
   375  	return respCfg
   376  }
   377  
   378  // GetConfigFile proxies access to the given configuration file to the underlying config store.
   379  func (a *App) GetConfigFile(name string) ([]byte, error) {
   380  	data, err := a.Srv().configStore.GetFile(name)
   381  	if err != nil {
   382  		return nil, errors.Wrapf(err, "failed to get config file %s", name)
   383  	}
   384  
   385  	return data, nil
   386  }
   387  
   388  // GetSanitizedConfig gets the configuration for a system admin without any secrets.
   389  func (a *App) GetSanitizedConfig() *model.Config {
   390  	cfg := a.Config().Clone()
   391  	cfg.Sanitize()
   392  
   393  	return cfg
   394  }
   395  
   396  // GetEnvironmentConfig returns a map of configuration keys whose values have been overridden by an environment variable.
   397  func (a *App) GetEnvironmentConfig() map[string]interface{} {
   398  	return a.EnvironmentConfig()
   399  }
   400  
   401  // SaveConfig replaces the active configuration, optionally notifying cluster peers.
   402  func (s *Server) SaveConfig(newCfg *model.Config, sendConfigChangeClusterMessage bool) *model.AppError {
   403  	oldCfg, err := s.configStore.Set(newCfg)
   404  	if errors.Cause(err) == config.ErrReadOnlyConfiguration {
   405  		return model.NewAppError("saveConfig", "ent.cluster.save_config.error", nil, err.Error(), http.StatusForbidden)
   406  	} else if err != nil {
   407  		return model.NewAppError("saveConfig", "app.save_config.app_error", nil, err.Error(), http.StatusInternalServerError)
   408  	}
   409  
   410  	if s.Metrics != nil {
   411  		if *s.Config().MetricsSettings.Enable {
   412  			s.Metrics.StartServer()
   413  		} else {
   414  			s.Metrics.StopServer()
   415  		}
   416  	}
   417  
   418  	if s.Cluster != nil {
   419  		newCfg = s.configStore.RemoveEnvironmentOverrides(newCfg)
   420  		oldCfg = s.configStore.RemoveEnvironmentOverrides(oldCfg)
   421  		err := s.Cluster.ConfigChanged(oldCfg, newCfg, sendConfigChangeClusterMessage)
   422  		if err != nil {
   423  			return err
   424  		}
   425  	}
   426  
   427  	return nil
   428  }
   429  
   430  // SaveConfig replaces the active configuration, optionally notifying cluster peers.
   431  func (a *App) SaveConfig(newCfg *model.Config, sendConfigChangeClusterMessage bool) *model.AppError {
   432  	return a.Srv().SaveConfig(newCfg, sendConfigChangeClusterMessage)
   433  }
   434  
   435  func (a *App) HandleMessageExportConfig(cfg *model.Config, appCfg *model.Config) {
   436  	// If the Message Export feature has been toggled in the System Console, rewrite the ExportFromTimestamp field to an
   437  	// appropriate value. The rewriting occurs here to ensure it doesn't affect values written to the config file
   438  	// directly and not through the System Console UI.
   439  	if *cfg.MessageExportSettings.EnableExport != *appCfg.MessageExportSettings.EnableExport {
   440  		if *cfg.MessageExportSettings.EnableExport && *cfg.MessageExportSettings.ExportFromTimestamp == int64(0) {
   441  			// When the feature is toggled on, use the current timestamp as the start time for future exports.
   442  			cfg.MessageExportSettings.ExportFromTimestamp = model.NewInt64(model.GetMillis())
   443  		} else if !*cfg.MessageExportSettings.EnableExport {
   444  			// When the feature is disabled, reset the timestamp so that the timestamp will be set if
   445  			// the feature is re-enabled from the System Console in future.
   446  			cfg.MessageExportSettings.ExportFromTimestamp = model.NewInt64(0)
   447  		}
   448  	}
   449  }