github.com/vnforks/kid/v5@v5.22.1-0.20200408055009-b89d99c65676/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/vnforks/kid/v5/config"
    24  	"github.com/vnforks/kid/v5/mlog"
    25  	"github.com/vnforks/kid/v5/model"
    26  	"github.com/vnforks/kid/v5/utils"
    27  )
    28  
    29  const (
    30  	ERROR_TERMS_OF_SERVICE_NO_ROWS_FOUND = "store.sql_terms_of_service_store.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
    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
    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 (a *App) ensurePostActionCookieSecret() error {
   110  	if a.Srv().postActionCookieSecret != nil {
   111  		return nil
   112  	}
   113  
   114  	var secret *model.SystemPostActionCookieSecret
   115  
   116  	value, err := a.Srv().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 := a.Srv().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 := a.Srv().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  	a.Srv().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 (a *App) ensureAsymmetricSigningKey() error {
   169  	if a.Srv().asymmetricSigningKey != nil {
   170  		return nil
   171  	}
   172  
   173  	var key *model.SystemAsymmetricSigningKey
   174  
   175  	value, err := a.Srv().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 := a.Srv().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 := a.Srv().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  	a.Srv().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  	a.regenerateClientConfig()
   241  	return nil
   242  }
   243  
   244  func (a *App) ensureInstallationDate() error {
   245  	_, err := a.getSystemInstallDate()
   246  	if err == nil {
   247  		return nil
   248  	}
   249  
   250  	installDate, err := a.Srv().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 = a.Srv().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  // AsymmetricSigningKey will return a private key that can be used for asymmetric signing.
   269  func (s *Server) AsymmetricSigningKey() *ecdsa.PrivateKey {
   270  	return s.asymmetricSigningKey
   271  }
   272  
   273  func (a *App) AsymmetricSigningKey() *ecdsa.PrivateKey {
   274  	return a.Srv().AsymmetricSigningKey()
   275  }
   276  
   277  func (s *Server) PostActionCookieSecret() []byte {
   278  	return s.postActionCookieSecret
   279  }
   280  
   281  func (a *App) PostActionCookieSecret() []byte {
   282  	return a.Srv().PostActionCookieSecret()
   283  }
   284  
   285  func (a *App) regenerateClientConfig() {
   286  	clientConfig := config.GenerateClientConfig(a.Config(), a.DiagnosticId(), a.License())
   287  	limitedClientConfig := config.GenerateLimitedClientConfig(a.Config(), a.DiagnosticId(), a.License())
   288  
   289  	if clientConfig["EnableCustomTermsOfService"] == "true" {
   290  		termsOfService, err := a.GetLatestTermsOfService()
   291  		if err != nil {
   292  			mlog.Err(err)
   293  		} else {
   294  			clientConfig["CustomTermsOfServiceId"] = termsOfService.Id
   295  			limitedClientConfig["CustomTermsOfServiceId"] = termsOfService.Id
   296  		}
   297  	}
   298  
   299  	if key := a.AsymmetricSigningKey(); key != nil {
   300  		der, _ := x509.MarshalPKIXPublicKey(&key.PublicKey)
   301  		clientConfig["AsymmetricSigningPublicKey"] = base64.StdEncoding.EncodeToString(der)
   302  		limitedClientConfig["AsymmetricSigningPublicKey"] = base64.StdEncoding.EncodeToString(der)
   303  	}
   304  
   305  	clientConfigJSON, _ := json.Marshal(clientConfig)
   306  	a.Srv().clientConfig = clientConfig
   307  	a.Srv().limitedClientConfig = limitedClientConfig
   308  	a.Srv().clientConfigHash = fmt.Sprintf("%x", md5.Sum(clientConfigJSON))
   309  }
   310  
   311  func (a *App) GetCookieDomain() string {
   312  	if *a.Config().ServiceSettings.AllowCookiesForSubdomains {
   313  		if siteURL, err := url.Parse(*a.Config().ServiceSettings.SiteURL); err == nil {
   314  			return siteURL.Hostname()
   315  		}
   316  	}
   317  	return ""
   318  }
   319  
   320  func (a *App) GetSiteURL() string {
   321  	return *a.Config().ServiceSettings.SiteURL
   322  }
   323  
   324  // ClientConfigWithComputed gets the configuration in a format suitable for sending to the client.
   325  func (a *App) ClientConfigWithComputed() map[string]string {
   326  	respCfg := map[string]string{}
   327  	for k, v := range a.ClientConfig() {
   328  		respCfg[k] = v
   329  	}
   330  
   331  	// These properties are not configurable, but nevertheless represent configuration expected
   332  	// by the client.
   333  	respCfg["NoAccounts"] = strconv.FormatBool(a.IsFirstUserAccount())
   334  	respCfg["MaxPostSize"] = strconv.Itoa(a.MaxPostSize())
   335  	respCfg["InstallationDate"] = ""
   336  	if installationDate, err := a.getSystemInstallDate(); err == nil {
   337  		respCfg["InstallationDate"] = strconv.FormatInt(installationDate, 10)
   338  	}
   339  
   340  	return respCfg
   341  }
   342  
   343  // LimitedClientConfigWithComputed gets the configuration in a format suitable for sending to the client.
   344  func (a *App) LimitedClientConfigWithComputed() map[string]string {
   345  	respCfg := map[string]string{}
   346  	for k, v := range a.LimitedClientConfig() {
   347  		respCfg[k] = v
   348  	}
   349  
   350  	// These properties are not configurable, but nevertheless represent configuration expected
   351  	// by the client.
   352  	respCfg["NoAccounts"] = strconv.FormatBool(a.IsFirstUserAccount())
   353  
   354  	return respCfg
   355  }
   356  
   357  // GetConfigFile proxies access to the given configuration file to the underlying config store.
   358  func (a *App) GetConfigFile(name string) ([]byte, error) {
   359  	data, err := a.Srv().configStore.GetFile(name)
   360  	if err != nil {
   361  		return nil, errors.Wrapf(err, "failed to get config file %s", name)
   362  	}
   363  
   364  	return data, nil
   365  }
   366  
   367  // GetSanitizedConfig gets the configuration for a system admin without any secrets.
   368  func (a *App) GetSanitizedConfig() *model.Config {
   369  	cfg := a.Config().Clone()
   370  	cfg.Sanitize()
   371  
   372  	return cfg
   373  }
   374  
   375  // GetEnvironmentConfig returns a map of configuration keys whose values have been overridden by an environment variable.
   376  func (a *App) GetEnvironmentConfig() map[string]interface{} {
   377  	return a.EnvironmentConfig()
   378  }
   379  
   380  // SaveConfig replaces the active configuration, optionally notifying cluster peers.
   381  func (a *App) SaveConfig(newCfg *model.Config, sendConfigChangeClusterMessage bool) *model.AppError {
   382  	oldCfg, err := a.Srv().configStore.Set(newCfg)
   383  	if errors.Cause(err) == config.ErrReadOnlyConfiguration {
   384  		return model.NewAppError("saveConfig", "ent.cluster.save_config.error", nil, err.Error(), http.StatusForbidden)
   385  	} else if err != nil {
   386  		return model.NewAppError("saveConfig", "app.save_config.app_error", nil, err.Error(), http.StatusInternalServerError)
   387  	}
   388  
   389  	if a.Metrics() != nil {
   390  		if *a.Config().MetricsSettings.Enable {
   391  			a.Metrics().StartServer()
   392  		} else {
   393  			a.Metrics().StopServer()
   394  		}
   395  	}
   396  
   397  	if a.Cluster() != nil {
   398  		newCfg = a.Srv().configStore.RemoveEnvironmentOverrides(newCfg)
   399  		oldCfg = a.Srv().configStore.RemoveEnvironmentOverrides(oldCfg)
   400  		err := a.Cluster().ConfigChanged(oldCfg, newCfg, sendConfigChangeClusterMessage)
   401  		if err != nil {
   402  			return err
   403  		}
   404  	}
   405  
   406  	return nil
   407  }
   408  
   409  func (a *App) HandleMessageExportConfig(cfg *model.Config, appCfg *model.Config) {
   410  	// If the Message Export feature has been toggled in the System Console, rewrite the ExportFromTimestamp field to an
   411  	// appropriate value. The rewriting occurs here to ensure it doesn't affect values written to the config file
   412  	// directly and not through the System Console UI.
   413  	if *cfg.MessageExportSettings.EnableExport != *appCfg.MessageExportSettings.EnableExport {
   414  		if *cfg.MessageExportSettings.EnableExport && *cfg.MessageExportSettings.ExportFromTimestamp == int64(0) {
   415  			// When the feature is toggled on, use the current timestamp as the start time for future exports.
   416  			cfg.MessageExportSettings.ExportFromTimestamp = model.NewInt64(model.GetMillis())
   417  		} else if !*cfg.MessageExportSettings.EnableExport {
   418  			// When the feature is disabled, reset the timestamp so that the timestamp will be set if
   419  			// the feature is re-enabled from the System Console in future.
   420  			cfg.MessageExportSettings.ExportFromTimestamp = model.NewInt64(0)
   421  		}
   422  	}
   423  }