github.com/adacta-ru/mattermost-server/v6@v6.0.0/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  	"strconv"
    18  	"time"
    19  
    20  	"github.com/pkg/errors"
    21  
    22  	"github.com/adacta-ru/mattermost-server/v6/config"
    23  	"github.com/adacta-ru/mattermost-server/v6/mlog"
    24  	"github.com/adacta-ru/mattermost-server/v6/model"
    25  	"github.com/adacta-ru/mattermost-server/v6/utils"
    26  )
    27  
    28  const (
    29  	ERROR_TERMS_OF_SERVICE_NO_ROWS_FOUND = "app.terms_of_service.get.no_rows.app_error"
    30  )
    31  
    32  func (s *Server) Config() *model.Config {
    33  	return s.configStore.Get()
    34  }
    35  
    36  func (a *App) Config() *model.Config {
    37  	return a.Srv().Config()
    38  }
    39  
    40  func (s *Server) EnvironmentConfig() map[string]interface{} {
    41  	return s.configStore.GetEnvironmentOverrides()
    42  }
    43  
    44  func (a *App) EnvironmentConfig() map[string]interface{} {
    45  	return a.Srv().EnvironmentConfig()
    46  }
    47  
    48  func (s *Server) UpdateConfig(f func(*model.Config)) {
    49  	old := s.Config()
    50  	updated := old.Clone()
    51  	f(updated)
    52  	if _, err := s.configStore.Set(updated); err != nil {
    53  		mlog.Error("Failed to update config", mlog.Err(err))
    54  	}
    55  }
    56  
    57  func (a *App) UpdateConfig(f func(*model.Config)) {
    58  	a.Srv().UpdateConfig(f)
    59  }
    60  
    61  func (s *Server) ReloadConfig() error {
    62  	if err := s.configStore.Load(); err != nil {
    63  		return err
    64  	}
    65  	return nil
    66  }
    67  
    68  func (a *App) ReloadConfig() error {
    69  	return a.Srv().ReloadConfig()
    70  }
    71  
    72  func (a *App) ClientConfig() map[string]string {
    73  	return a.Srv().clientConfig.Load().(map[string]string)
    74  }
    75  
    76  func (a *App) ClientConfigHash() string {
    77  	return a.Srv().ClientConfigHash()
    78  }
    79  
    80  func (a *App) LimitedClientConfig() map[string]string {
    81  	return a.Srv().limitedClientConfig.Load().(map[string]string)
    82  }
    83  
    84  // Registers a function with a given listener to be called when the config is reloaded and may have changed. The function
    85  // will be called with two arguments: the old config and the new config. AddConfigListener returns a unique ID
    86  // for the listener that can later be used to remove it.
    87  func (s *Server) AddConfigListener(listener func(*model.Config, *model.Config)) string {
    88  	return s.configStore.AddListener(listener)
    89  }
    90  
    91  func (a *App) AddConfigListener(listener func(*model.Config, *model.Config)) string {
    92  	return a.Srv().AddConfigListener(listener)
    93  }
    94  
    95  // Removes a listener function by the unique ID returned when AddConfigListener was called
    96  func (s *Server) RemoveConfigListener(id string) {
    97  	s.configStore.RemoveListener(id)
    98  }
    99  
   100  func (a *App) RemoveConfigListener(id string) {
   101  	a.Srv().RemoveConfigListener(id)
   102  }
   103  
   104  // ensurePostActionCookieSecret ensures that the key for encrypting PostActionCookie exists
   105  // and future calls to PostActionCookieSecret will always return a valid key, same on all
   106  // servers in the cluster
   107  func (s *Server) ensurePostActionCookieSecret() error {
   108  	if s.postActionCookieSecret != nil {
   109  		return nil
   110  	}
   111  
   112  	var secret *model.SystemPostActionCookieSecret
   113  
   114  	value, err := s.Store.System().GetByName(model.SYSTEM_POST_ACTION_COOKIE_SECRET)
   115  	if err == nil {
   116  		if err := json.Unmarshal([]byte(value.Value), &secret); err != nil {
   117  			return err
   118  		}
   119  	}
   120  
   121  	// If we don't already have a key, try to generate one.
   122  	if secret == nil {
   123  		newSecret := &model.SystemPostActionCookieSecret{
   124  			Secret: make([]byte, 32),
   125  		}
   126  		_, err := rand.Reader.Read(newSecret.Secret)
   127  		if err != nil {
   128  			return err
   129  		}
   130  
   131  		system := &model.System{
   132  			Name: model.SYSTEM_POST_ACTION_COOKIE_SECRET,
   133  		}
   134  		v, err := json.Marshal(newSecret)
   135  		if err != nil {
   136  			return err
   137  		}
   138  		system.Value = string(v)
   139  		// If we were able to save the key, use it, otherwise log the error.
   140  		if err = s.Store.System().Save(system); err != nil {
   141  			mlog.Error("Failed to save PostActionCookieSecret", mlog.Err(err))
   142  		} else {
   143  			secret = newSecret
   144  		}
   145  	}
   146  
   147  	// If we weren't able to save a new key above, another server must have beat us to it. Get the
   148  	// key from the database, and if that fails, error out.
   149  	if secret == nil {
   150  		value, err := s.Store.System().GetByName(model.SYSTEM_POST_ACTION_COOKIE_SECRET)
   151  		if err != nil {
   152  			return err
   153  		}
   154  
   155  		if err := json.Unmarshal([]byte(value.Value), &secret); err != nil {
   156  			return err
   157  		}
   158  	}
   159  
   160  	s.postActionCookieSecret = secret.Secret
   161  	return nil
   162  }
   163  
   164  // ensureAsymmetricSigningKey ensures that an asymmetric signing key exists and future calls to
   165  // AsymmetricSigningKey will always return a valid signing key.
   166  func (s *Server) ensureAsymmetricSigningKey() error {
   167  	if s.AsymmetricSigningKey() != nil {
   168  		return nil
   169  	}
   170  
   171  	var key *model.SystemAsymmetricSigningKey
   172  
   173  	value, err := s.Store.System().GetByName(model.SYSTEM_ASYMMETRIC_SIGNING_KEY)
   174  	if err == nil {
   175  		if err := json.Unmarshal([]byte(value.Value), &key); err != nil {
   176  			return err
   177  		}
   178  	}
   179  
   180  	// If we don't already have a key, try to generate one.
   181  	if key == nil {
   182  		newECDSAKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
   183  		if err != nil {
   184  			return err
   185  		}
   186  		newKey := &model.SystemAsymmetricSigningKey{
   187  			ECDSAKey: &model.SystemECDSAKey{
   188  				Curve: "P-256",
   189  				X:     newECDSAKey.X,
   190  				Y:     newECDSAKey.Y,
   191  				D:     newECDSAKey.D,
   192  			},
   193  		}
   194  		system := &model.System{
   195  			Name: model.SYSTEM_ASYMMETRIC_SIGNING_KEY,
   196  		}
   197  		v, err := json.Marshal(newKey)
   198  		if err != nil {
   199  			return err
   200  		}
   201  		system.Value = string(v)
   202  		// If we were able to save the key, use it, otherwise log the error.
   203  		if err = s.Store.System().Save(system); err != nil {
   204  			mlog.Error("Failed to save AsymmetricSigningKey", mlog.Err(err))
   205  		} else {
   206  			key = newKey
   207  		}
   208  	}
   209  
   210  	// If we weren't able to save a new key above, another server must have beat us to it. Get the
   211  	// key from the database, and if that fails, error out.
   212  	if key == nil {
   213  		value, err := s.Store.System().GetByName(model.SYSTEM_ASYMMETRIC_SIGNING_KEY)
   214  		if err != nil {
   215  			return err
   216  		}
   217  
   218  		if err := json.Unmarshal([]byte(value.Value), &key); err != nil {
   219  			return err
   220  		}
   221  	}
   222  
   223  	var curve elliptic.Curve
   224  	switch key.ECDSAKey.Curve {
   225  	case "P-256":
   226  		curve = elliptic.P256()
   227  	default:
   228  		return fmt.Errorf("unknown curve: " + key.ECDSAKey.Curve)
   229  	}
   230  	s.asymmetricSigningKey.Store(&ecdsa.PrivateKey{
   231  		PublicKey: ecdsa.PublicKey{
   232  			Curve: curve,
   233  			X:     key.ECDSAKey.X,
   234  			Y:     key.ECDSAKey.Y,
   235  		},
   236  		D: key.ECDSAKey.D,
   237  	})
   238  	s.regenerateClientConfig()
   239  	return nil
   240  }
   241  
   242  func (s *Server) ensureInstallationDate() error {
   243  	_, appErr := s.getSystemInstallDate()
   244  	if appErr == nil {
   245  		return nil
   246  	}
   247  
   248  	installDate, nErr := s.Store.User().InferSystemInstallDate()
   249  	var installationDate int64
   250  	if nErr == nil && installDate > 0 {
   251  		installationDate = installDate
   252  	} else {
   253  		installationDate = utils.MillisFromTime(time.Now())
   254  	}
   255  
   256  	if err := s.Store.System().SaveOrUpdate(&model.System{
   257  		Name:  model.SYSTEM_INSTALLATION_DATE_KEY,
   258  		Value: strconv.FormatInt(installationDate, 10),
   259  	}); err != nil {
   260  		return err
   261  	}
   262  	return nil
   263  }
   264  
   265  func (s *Server) ensureFirstServerRunTimestamp() error {
   266  	_, appErr := s.getFirstServerRunTimestamp()
   267  	if appErr == nil {
   268  		return nil
   269  	}
   270  
   271  	if err := s.Store.System().SaveOrUpdate(&model.System{
   272  		Name:  model.SYSTEM_FIRST_SERVER_RUN_TIMESTAMP_KEY,
   273  		Value: strconv.FormatInt(utils.MillisFromTime(time.Now()), 10),
   274  	}); err != nil {
   275  		return err
   276  	}
   277  	return nil
   278  }
   279  
   280  // AsymmetricSigningKey will return a private key that can be used for asymmetric signing.
   281  func (s *Server) AsymmetricSigningKey() *ecdsa.PrivateKey {
   282  	if key := s.asymmetricSigningKey.Load(); key != nil {
   283  		return key.(*ecdsa.PrivateKey)
   284  	}
   285  	return nil
   286  }
   287  
   288  func (a *App) AsymmetricSigningKey() *ecdsa.PrivateKey {
   289  	return a.Srv().AsymmetricSigningKey()
   290  }
   291  
   292  func (s *Server) PostActionCookieSecret() []byte {
   293  	return s.postActionCookieSecret
   294  }
   295  
   296  func (a *App) PostActionCookieSecret() []byte {
   297  	return a.Srv().PostActionCookieSecret()
   298  }
   299  
   300  func (s *Server) regenerateClientConfig() {
   301  	clientConfig := config.GenerateClientConfig(s.Config(), s.TelemetryId(), s.License())
   302  	limitedClientConfig := config.GenerateLimitedClientConfig(s.Config(), s.TelemetryId(), s.License())
   303  
   304  	if clientConfig["EnableCustomTermsOfService"] == "true" {
   305  		termsOfService, err := s.Store.TermsOfService().GetLatest(true)
   306  		if err != nil {
   307  			mlog.Err(err)
   308  		} else {
   309  			clientConfig["CustomTermsOfServiceId"] = termsOfService.Id
   310  			limitedClientConfig["CustomTermsOfServiceId"] = termsOfService.Id
   311  		}
   312  	}
   313  
   314  	if key := s.AsymmetricSigningKey(); key != nil {
   315  		der, _ := x509.MarshalPKIXPublicKey(&key.PublicKey)
   316  		clientConfig["AsymmetricSigningPublicKey"] = base64.StdEncoding.EncodeToString(der)
   317  		limitedClientConfig["AsymmetricSigningPublicKey"] = base64.StdEncoding.EncodeToString(der)
   318  	}
   319  
   320  	clientConfigJSON, _ := json.Marshal(clientConfig)
   321  	s.clientConfig.Store(clientConfig)
   322  	s.limitedClientConfig.Store(limitedClientConfig)
   323  	s.clientConfigHash.Store(fmt.Sprintf("%x", md5.Sum(clientConfigJSON)))
   324  }
   325  
   326  func (a *App) GetCookieDomain() string {
   327  	if *a.Config().ServiceSettings.AllowCookiesForSubdomains {
   328  		if siteURL, err := url.Parse(*a.Config().ServiceSettings.SiteURL); err == nil {
   329  			return siteURL.Hostname()
   330  		}
   331  	}
   332  	return ""
   333  }
   334  
   335  func (a *App) GetSiteURL() string {
   336  	return *a.Config().ServiceSettings.SiteURL
   337  }
   338  
   339  // ClientConfigWithComputed gets the configuration in a format suitable for sending to the client.
   340  func (s *Server) ClientConfigWithComputed() map[string]string {
   341  	respCfg := map[string]string{}
   342  	for k, v := range s.clientConfig.Load().(map[string]string) {
   343  		respCfg[k] = v
   344  	}
   345  
   346  	// These properties are not configurable, but nevertheless represent configuration expected
   347  	// by the client.
   348  	respCfg["NoAccounts"] = strconv.FormatBool(s.IsFirstUserAccount())
   349  	respCfg["MaxPostSize"] = strconv.Itoa(s.MaxPostSize())
   350  	respCfg["UpgradedFromTE"] = strconv.FormatBool(s.isUpgradedFromTE())
   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  }