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 }