github.com/crspeller/mattermost-server@v0.0.0-20190328001957-a200beb3d111/app/config.go (about)

     1  // Copyright (c) 2016-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/crspeller/mattermost-server/config"
    24  	"github.com/crspeller/mattermost-server/mlog"
    25  	"github.com/crspeller/mattermost-server/model"
    26  	"github.com/crspeller/mattermost-server/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 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 PostAcrionCookieSecret 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  	result := <-a.Srv.Store.System().GetByName(model.SYSTEM_POST_ACTION_COOKIE_SECRET)
   117  	if result.Err == nil {
   118  		if err := json.Unmarshal([]byte(result.Data.(*model.System).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 result = <-a.Srv.Store.System().Save(system); result.Err == nil {
   142  			// If we were able to save the key, use it, otherwise ignore the error.
   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  		result := <-a.Srv.Store.System().GetByName(model.SYSTEM_POST_ACTION_COOKIE_SECRET)
   151  		if result.Err != nil {
   152  			return result.Err
   153  		}
   154  
   155  		if err := json.Unmarshal([]byte(result.Data.(*model.System).Value), &secret); err != nil {
   156  			return err
   157  		}
   158  	}
   159  
   160  	a.Srv.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 (a *App) ensureAsymmetricSigningKey() error {
   167  	if a.Srv.asymmetricSigningKey != nil {
   168  		return nil
   169  	}
   170  
   171  	var key *model.SystemAsymmetricSigningKey
   172  
   173  	result := <-a.Srv.Store.System().GetByName(model.SYSTEM_ASYMMETRIC_SIGNING_KEY)
   174  	if result.Err == nil {
   175  		if err := json.Unmarshal([]byte(result.Data.(*model.System).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 result = <-a.Srv.Store.System().Save(system); result.Err == nil {
   203  			// If we were able to save the key, use it, otherwise ignore the error.
   204  			key = newKey
   205  		}
   206  	}
   207  
   208  	// If we weren't able to save a new key above, another server must have beat us to it. Get the
   209  	// key from the database, and if that fails, error out.
   210  	if key == nil {
   211  		result := <-a.Srv.Store.System().GetByName(model.SYSTEM_ASYMMETRIC_SIGNING_KEY)
   212  		if result.Err != nil {
   213  			return result.Err
   214  		}
   215  
   216  		if err := json.Unmarshal([]byte(result.Data.(*model.System).Value), &key); err != nil {
   217  			return err
   218  		}
   219  	}
   220  
   221  	var curve elliptic.Curve
   222  	switch key.ECDSAKey.Curve {
   223  	case "P-256":
   224  		curve = elliptic.P256()
   225  	default:
   226  		return fmt.Errorf("unknown curve: " + key.ECDSAKey.Curve)
   227  	}
   228  	a.Srv.asymmetricSigningKey = &ecdsa.PrivateKey{
   229  		PublicKey: ecdsa.PublicKey{
   230  			Curve: curve,
   231  			X:     key.ECDSAKey.X,
   232  			Y:     key.ECDSAKey.Y,
   233  		},
   234  		D: key.ECDSAKey.D,
   235  	}
   236  	a.regenerateClientConfig()
   237  	return nil
   238  }
   239  
   240  func (a *App) ensureInstallationDate() error {
   241  	_, err := a.getSystemInstallDate()
   242  	if err == nil {
   243  		return nil
   244  	}
   245  
   246  	result := <-a.Srv.Store.User().InferSystemInstallDate()
   247  	var installationDate int64
   248  	if result.Err == nil && result.Data.(int64) > 0 {
   249  		installationDate = result.Data.(int64)
   250  	} else {
   251  		installationDate = utils.MillisFromTime(time.Now())
   252  	}
   253  
   254  	result = <-a.Srv.Store.System().SaveOrUpdate(&model.System{
   255  		Name:  model.SYSTEM_INSTALLATION_DATE_KEY,
   256  		Value: strconv.FormatInt(installationDate, 10),
   257  	})
   258  	if result.Err != nil {
   259  		return result.Err
   260  	}
   261  	return nil
   262  }
   263  
   264  // AsymmetricSigningKey will return a private key that can be used for asymmetric signing.
   265  func (s *Server) AsymmetricSigningKey() *ecdsa.PrivateKey {
   266  	return s.asymmetricSigningKey
   267  }
   268  
   269  func (a *App) AsymmetricSigningKey() *ecdsa.PrivateKey {
   270  	return a.Srv.AsymmetricSigningKey()
   271  }
   272  
   273  func (s *Server) PostActionCookieSecret() []byte {
   274  	return s.postActionCookieSecret
   275  }
   276  
   277  func (a *App) PostActionCookieSecret() []byte {
   278  	return a.Srv.PostActionCookieSecret()
   279  }
   280  
   281  func (a *App) regenerateClientConfig() {
   282  	clientConfig := config.GenerateClientConfig(a.Config(), a.DiagnosticId(), a.License())
   283  	limitedClientConfig := config.GenerateLimitedClientConfig(a.Config(), a.DiagnosticId(), a.License())
   284  
   285  	if clientConfig["EnableCustomTermsOfService"] == "true" {
   286  		termsOfService, err := a.GetLatestTermsOfService()
   287  		if err != nil {
   288  			mlog.Err(err)
   289  		} else {
   290  			clientConfig["CustomTermsOfServiceId"] = termsOfService.Id
   291  			limitedClientConfig["CustomTermsOfServiceId"] = termsOfService.Id
   292  		}
   293  	}
   294  
   295  	if key := a.AsymmetricSigningKey(); key != nil {
   296  		der, _ := x509.MarshalPKIXPublicKey(&key.PublicKey)
   297  		clientConfig["AsymmetricSigningPublicKey"] = base64.StdEncoding.EncodeToString(der)
   298  		limitedClientConfig["AsymmetricSigningPublicKey"] = base64.StdEncoding.EncodeToString(der)
   299  	}
   300  
   301  	clientConfigJSON, _ := json.Marshal(clientConfig)
   302  	a.Srv.clientConfig = clientConfig
   303  	a.Srv.limitedClientConfig = limitedClientConfig
   304  	a.Srv.clientConfigHash = fmt.Sprintf("%x", md5.Sum(clientConfigJSON))
   305  }
   306  
   307  func (a *App) GetCookieDomain() string {
   308  	if *a.Config().ServiceSettings.AllowCookiesForSubdomains {
   309  		if siteURL, err := url.Parse(*a.Config().ServiceSettings.SiteURL); err == nil {
   310  			return siteURL.Hostname()
   311  		}
   312  	}
   313  	return ""
   314  }
   315  
   316  func (a *App) GetSiteURL() string {
   317  	return *a.Config().ServiceSettings.SiteURL
   318  }
   319  
   320  // ClientConfigWithComputed gets the configuration in a format suitable for sending to the client.
   321  func (a *App) ClientConfigWithComputed() map[string]string {
   322  	respCfg := map[string]string{}
   323  	for k, v := range a.ClientConfig() {
   324  		respCfg[k] = v
   325  	}
   326  
   327  	// These properties are not configurable, but nevertheless represent configuration expected
   328  	// by the client.
   329  	respCfg["NoAccounts"] = strconv.FormatBool(a.IsFirstUserAccount())
   330  	respCfg["MaxPostSize"] = strconv.Itoa(a.MaxPostSize())
   331  	respCfg["InstallationDate"] = ""
   332  	if installationDate, err := a.getSystemInstallDate(); err == nil {
   333  		respCfg["InstallationDate"] = strconv.FormatInt(installationDate, 10)
   334  	}
   335  
   336  	return respCfg
   337  }
   338  
   339  // LimitedClientConfigWithComputed gets the configuration in a format suitable for sending to the client.
   340  func (a *App) LimitedClientConfigWithComputed() map[string]string {
   341  	respCfg := map[string]string{}
   342  	for k, v := range a.LimitedClientConfig() {
   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(a.IsFirstUserAccount())
   349  
   350  	return respCfg
   351  }
   352  
   353  // GetConfigFile proxies access to the given configuration file to the underlying config store.
   354  func (a *App) GetConfigFile(name string) ([]byte, error) {
   355  	data, err := a.Srv.configStore.GetFile(name)
   356  	if err != nil {
   357  		return nil, errors.Wrapf(err, "failed to get config file %s", name)
   358  	}
   359  
   360  	return data, nil
   361  }
   362  
   363  // GetSanitizedConfig gets the configuration for a system admin without any secrets.
   364  func (a *App) GetSanitizedConfig() *model.Config {
   365  	cfg := a.Config().Clone()
   366  	cfg.Sanitize()
   367  
   368  	return cfg
   369  }
   370  
   371  // GetEnvironmentConfig returns a map of configuration keys whose values have been overridden by an environment variable.
   372  func (a *App) GetEnvironmentConfig() map[string]interface{} {
   373  	return a.EnvironmentConfig()
   374  }
   375  
   376  // SaveConfig replaces the active configuration, optionally notifying cluster peers.
   377  func (a *App) SaveConfig(newCfg *model.Config, sendConfigChangeClusterMessage bool) *model.AppError {
   378  	oldCfg, err := a.Srv.configStore.Set(newCfg)
   379  	if errors.Cause(err) == config.ErrReadOnlyConfiguration {
   380  		return model.NewAppError("saveConfig", "ent.cluster.save_config.error", nil, err.Error(), http.StatusForbidden)
   381  	} else if err != nil {
   382  		return model.NewAppError("saveConfig", "app.save_config.app_error", nil, err.Error(), http.StatusInternalServerError)
   383  	}
   384  
   385  	if a.Metrics != nil {
   386  		if *a.Config().MetricsSettings.Enable {
   387  			a.Metrics.StartServer()
   388  		} else {
   389  			a.Metrics.StopServer()
   390  		}
   391  	}
   392  
   393  	if a.Cluster != nil {
   394  		err := a.Cluster.ConfigChanged(oldCfg, newCfg, sendConfigChangeClusterMessage)
   395  		if err != nil {
   396  			return err
   397  		}
   398  	}
   399  
   400  	return nil
   401  }