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 }