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 }