github.com/keys-pub/mattermost-server@v4.10.10+incompatible/app/app.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 "fmt" 9 "html/template" 10 "net" 11 "net/http" 12 "reflect" 13 "strings" 14 "sync" 15 "sync/atomic" 16 17 "github.com/gorilla/mux" 18 "github.com/pkg/errors" 19 20 "github.com/mattermost/mattermost-server/einterfaces" 21 ejobs "github.com/mattermost/mattermost-server/einterfaces/jobs" 22 "github.com/mattermost/mattermost-server/jobs" 23 "github.com/mattermost/mattermost-server/mlog" 24 "github.com/mattermost/mattermost-server/model" 25 "github.com/mattermost/mattermost-server/plugin/pluginenv" 26 "github.com/mattermost/mattermost-server/store" 27 "github.com/mattermost/mattermost-server/store/sqlstore" 28 "github.com/mattermost/mattermost-server/utils" 29 ) 30 31 const ADVANCED_PERMISSIONS_MIGRATION_KEY = "AdvancedPermissionsMigrationComplete" 32 33 type App struct { 34 goroutineCount int32 35 goroutineExitSignal chan struct{} 36 37 Srv *Server 38 39 Log *mlog.Logger 40 41 PluginEnv *pluginenv.Environment 42 PluginConfigListenerId string 43 44 EmailBatching *EmailBatchingJob 45 46 Hubs []*Hub 47 HubsStopCheckingForDeadlock chan bool 48 49 Jobs *jobs.JobServer 50 51 AccountMigration einterfaces.AccountMigrationInterface 52 Brand einterfaces.BrandInterface 53 Cluster einterfaces.ClusterInterface 54 Compliance einterfaces.ComplianceInterface 55 DataRetention einterfaces.DataRetentionInterface 56 Elasticsearch einterfaces.ElasticsearchInterface 57 Emoji einterfaces.EmojiInterface 58 Ldap einterfaces.LdapInterface 59 MessageExport einterfaces.MessageExportInterface 60 Metrics einterfaces.MetricsInterface 61 Mfa einterfaces.MfaInterface 62 Saml einterfaces.SamlInterface 63 64 config atomic.Value 65 envConfig map[string]interface{} 66 configFile string 67 configListeners map[string]func(*model.Config, *model.Config) 68 69 licenseValue atomic.Value 70 clientLicenseValue atomic.Value 71 licenseListeners map[string]func() 72 73 timezones atomic.Value 74 75 siteURL string 76 77 newStore func() store.Store 78 79 htmlTemplateWatcher *utils.HTMLTemplateWatcher 80 sessionCache *utils.Cache 81 configListenerId string 82 licenseListenerId string 83 logListenerId string 84 disableConfigWatch bool 85 configWatcher *utils.ConfigWatcher 86 asymmetricSigningKey *ecdsa.PrivateKey 87 88 pluginCommands []*PluginCommand 89 pluginCommandsLock sync.RWMutex 90 91 clientConfig map[string]string 92 clientConfigHash string 93 diagnosticId string 94 } 95 96 var appCount = 0 97 98 // New creates a new App. You must call Shutdown when you're done with it. 99 // XXX: For now, only one at a time is allowed as some resources are still shared. 100 func New(options ...Option) (outApp *App, outErr error) { 101 appCount++ 102 if appCount > 1 { 103 panic("Only one App should exist at a time. Did you forget to call Shutdown()?") 104 } 105 106 app := &App{ 107 goroutineExitSignal: make(chan struct{}, 1), 108 Srv: &Server{ 109 Router: mux.NewRouter(), 110 }, 111 sessionCache: utils.NewLru(model.SESSION_CACHE_SIZE), 112 configFile: "config.json", 113 configListeners: make(map[string]func(*model.Config, *model.Config)), 114 clientConfig: make(map[string]string), 115 licenseListeners: map[string]func(){}, 116 } 117 defer func() { 118 if outErr != nil { 119 app.Shutdown() 120 } 121 }() 122 123 for _, option := range options { 124 option(app) 125 } 126 127 if utils.T == nil { 128 if err := utils.TranslationsPreInit(); err != nil { 129 return nil, errors.Wrapf(err, "unable to load Mattermost translation files") 130 } 131 } 132 model.AppErrorInit(utils.T) 133 134 if err := app.LoadConfig(app.configFile); err != nil { 135 return nil, err 136 } 137 138 // Initalize logging 139 app.Log = mlog.NewLogger(utils.MloggerConfigFromLoggerConfig(&app.Config().LogSettings)) 140 141 // Redirect default golang logger to this logger 142 mlog.RedirectStdLog(app.Log) 143 144 // Use this app logger as the global logger (eventually remove all instances of global logging) 145 mlog.InitGlobalLogger(app.Log) 146 147 app.logListenerId = app.AddConfigListener(func(_, after *model.Config) { 148 app.Log.ChangeLevels(utils.MloggerConfigFromLoggerConfig(&after.LogSettings)) 149 }) 150 151 app.EnableConfigWatch() 152 153 app.LoadTimezones() 154 155 if err := utils.InitTranslations(app.Config().LocalizationSettings); err != nil { 156 return nil, errors.Wrapf(err, "unable to load Mattermost translation files") 157 } 158 159 app.configListenerId = app.AddConfigListener(func(_, _ *model.Config) { 160 app.configOrLicenseListener() 161 162 message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CONFIG_CHANGED, "", "", "", nil) 163 164 message.Add("config", app.ClientConfigWithComputed()) 165 app.Go(func() { 166 app.Publish(message) 167 }) 168 }) 169 app.licenseListenerId = app.AddLicenseListener(func() { 170 app.configOrLicenseListener() 171 172 message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_LICENSE_CHANGED, "", "", "", nil) 173 message.Add("license", app.GetSanitizedClientLicense()) 174 app.Go(func() { 175 app.Publish(message) 176 }) 177 178 }) 179 app.regenerateClientConfig() 180 181 mlog.Info("Server is initializing...") 182 183 app.initEnterprise() 184 185 if app.newStore == nil { 186 app.newStore = func() store.Store { 187 return store.NewLayeredStore(sqlstore.NewSqlSupplier(app.Config().SqlSettings, app.Metrics), app.Metrics, app.Cluster) 188 } 189 } 190 191 if htmlTemplateWatcher, err := utils.NewHTMLTemplateWatcher("templates"); err != nil { 192 mlog.Error(fmt.Sprintf("Failed to parse server templates %v", err)) 193 } else { 194 app.htmlTemplateWatcher = htmlTemplateWatcher 195 } 196 197 app.Srv.Store = app.newStore() 198 if err := app.ensureAsymmetricSigningKey(); err != nil { 199 return nil, errors.Wrapf(err, "unable to ensure asymmetric signing key") 200 } 201 202 app.initJobs() 203 204 app.initBuiltInPlugins() 205 app.Srv.Router.HandleFunc("/plugins/{plugin_id:[A-Za-z0-9\\_\\-\\.]+}", app.ServePluginRequest) 206 app.Srv.Router.HandleFunc("/plugins/{plugin_id:[A-Za-z0-9\\_\\-\\.]+}/{anything:.*}", app.ServePluginRequest) 207 208 app.Srv.Router.NotFoundHandler = http.HandlerFunc(app.Handle404) 209 210 app.Srv.WebSocketRouter = &WebSocketRouter{ 211 app: app, 212 handlers: make(map[string]webSocketHandler), 213 } 214 215 return app, nil 216 } 217 218 func (a *App) configOrLicenseListener() { 219 a.regenerateClientConfig() 220 } 221 222 func (a *App) Shutdown() { 223 appCount-- 224 225 mlog.Info("Stopping Server...") 226 227 a.StopServer() 228 a.HubStop() 229 230 a.ShutDownPlugins() 231 a.WaitForGoroutines() 232 233 if a.Srv.Store != nil { 234 a.Srv.Store.Close() 235 } 236 a.Srv = nil 237 238 if a.htmlTemplateWatcher != nil { 239 a.htmlTemplateWatcher.Close() 240 } 241 242 a.RemoveConfigListener(a.configListenerId) 243 a.RemoveLicenseListener(a.licenseListenerId) 244 a.RemoveConfigListener(a.logListenerId) 245 mlog.Info("Server stopped") 246 247 a.DisableConfigWatch() 248 } 249 250 var accountMigrationInterface func(*App) einterfaces.AccountMigrationInterface 251 252 func RegisterAccountMigrationInterface(f func(*App) einterfaces.AccountMigrationInterface) { 253 accountMigrationInterface = f 254 } 255 256 var brandInterface func(*App) einterfaces.BrandInterface 257 258 func RegisterBrandInterface(f func(*App) einterfaces.BrandInterface) { 259 brandInterface = f 260 } 261 262 var clusterInterface func(*App) einterfaces.ClusterInterface 263 264 func RegisterClusterInterface(f func(*App) einterfaces.ClusterInterface) { 265 clusterInterface = f 266 } 267 268 var complianceInterface func(*App) einterfaces.ComplianceInterface 269 270 func RegisterComplianceInterface(f func(*App) einterfaces.ComplianceInterface) { 271 complianceInterface = f 272 } 273 274 var dataRetentionInterface func(*App) einterfaces.DataRetentionInterface 275 276 func RegisterDataRetentionInterface(f func(*App) einterfaces.DataRetentionInterface) { 277 dataRetentionInterface = f 278 } 279 280 var elasticsearchInterface func(*App) einterfaces.ElasticsearchInterface 281 282 func RegisterElasticsearchInterface(f func(*App) einterfaces.ElasticsearchInterface) { 283 elasticsearchInterface = f 284 } 285 286 var emojiInterface func(*App) einterfaces.EmojiInterface 287 288 func RegisterEmojiInterface(f func(*App) einterfaces.EmojiInterface) { 289 emojiInterface = f 290 } 291 292 var jobsDataRetentionJobInterface func(*App) ejobs.DataRetentionJobInterface 293 294 func RegisterJobsDataRetentionJobInterface(f func(*App) ejobs.DataRetentionJobInterface) { 295 jobsDataRetentionJobInterface = f 296 } 297 298 var jobsMessageExportJobInterface func(*App) ejobs.MessageExportJobInterface 299 300 func RegisterJobsMessageExportJobInterface(f func(*App) ejobs.MessageExportJobInterface) { 301 jobsMessageExportJobInterface = f 302 } 303 304 var jobsElasticsearchAggregatorInterface func(*App) ejobs.ElasticsearchAggregatorInterface 305 306 func RegisterJobsElasticsearchAggregatorInterface(f func(*App) ejobs.ElasticsearchAggregatorInterface) { 307 jobsElasticsearchAggregatorInterface = f 308 } 309 310 var jobsElasticsearchIndexerInterface func(*App) ejobs.ElasticsearchIndexerInterface 311 312 func RegisterJobsElasticsearchIndexerInterface(f func(*App) ejobs.ElasticsearchIndexerInterface) { 313 jobsElasticsearchIndexerInterface = f 314 } 315 316 var jobsLdapSyncInterface func(*App) ejobs.LdapSyncInterface 317 318 func RegisterJobsLdapSyncInterface(f func(*App) ejobs.LdapSyncInterface) { 319 jobsLdapSyncInterface = f 320 } 321 322 var ldapInterface func(*App) einterfaces.LdapInterface 323 324 func RegisterLdapInterface(f func(*App) einterfaces.LdapInterface) { 325 ldapInterface = f 326 } 327 328 var messageExportInterface func(*App) einterfaces.MessageExportInterface 329 330 func RegisterMessageExportInterface(f func(*App) einterfaces.MessageExportInterface) { 331 messageExportInterface = f 332 } 333 334 var metricsInterface func(*App) einterfaces.MetricsInterface 335 336 func RegisterMetricsInterface(f func(*App) einterfaces.MetricsInterface) { 337 metricsInterface = f 338 } 339 340 var mfaInterface func(*App) einterfaces.MfaInterface 341 342 func RegisterMfaInterface(f func(*App) einterfaces.MfaInterface) { 343 mfaInterface = f 344 } 345 346 var samlInterface func(*App) einterfaces.SamlInterface 347 348 func RegisterSamlInterface(f func(*App) einterfaces.SamlInterface) { 349 samlInterface = f 350 } 351 352 func (a *App) initEnterprise() { 353 if accountMigrationInterface != nil { 354 a.AccountMigration = accountMigrationInterface(a) 355 } 356 if brandInterface != nil { 357 a.Brand = brandInterface(a) 358 } 359 if clusterInterface != nil { 360 a.Cluster = clusterInterface(a) 361 } 362 if complianceInterface != nil { 363 a.Compliance = complianceInterface(a) 364 } 365 if elasticsearchInterface != nil { 366 a.Elasticsearch = elasticsearchInterface(a) 367 } 368 if emojiInterface != nil { 369 a.Emoji = emojiInterface(a) 370 } 371 if ldapInterface != nil { 372 a.Ldap = ldapInterface(a) 373 a.AddConfigListener(func(_, cfg *model.Config) { 374 if err := utils.ValidateLdapFilter(cfg, a.Ldap); err != nil { 375 panic(utils.T(err.Id)) 376 } 377 }) 378 } 379 if messageExportInterface != nil { 380 a.MessageExport = messageExportInterface(a) 381 } 382 if metricsInterface != nil { 383 a.Metrics = metricsInterface(a) 384 } 385 if mfaInterface != nil { 386 a.Mfa = mfaInterface(a) 387 } 388 if samlInterface != nil { 389 a.Saml = samlInterface(a) 390 a.AddConfigListener(func(_, cfg *model.Config) { 391 a.Saml.ConfigureSP() 392 }) 393 } 394 if dataRetentionInterface != nil { 395 a.DataRetention = dataRetentionInterface(a) 396 } 397 } 398 399 func (a *App) initJobs() { 400 a.Jobs = jobs.NewJobServer(a, a.Srv.Store) 401 if jobsDataRetentionJobInterface != nil { 402 a.Jobs.DataRetentionJob = jobsDataRetentionJobInterface(a) 403 } 404 if jobsMessageExportJobInterface != nil { 405 a.Jobs.MessageExportJob = jobsMessageExportJobInterface(a) 406 } 407 if jobsElasticsearchAggregatorInterface != nil { 408 a.Jobs.ElasticsearchAggregator = jobsElasticsearchAggregatorInterface(a) 409 } 410 if jobsElasticsearchIndexerInterface != nil { 411 a.Jobs.ElasticsearchIndexer = jobsElasticsearchIndexerInterface(a) 412 } 413 if jobsLdapSyncInterface != nil { 414 a.Jobs.LdapSync = jobsLdapSyncInterface(a) 415 } 416 } 417 418 func (a *App) DiagnosticId() string { 419 return a.diagnosticId 420 } 421 422 func (a *App) SetDiagnosticId(id string) { 423 a.diagnosticId = id 424 } 425 426 func (a *App) EnsureDiagnosticId() { 427 if a.diagnosticId != "" { 428 return 429 } 430 if result := <-a.Srv.Store.System().Get(); result.Err == nil { 431 props := result.Data.(model.StringMap) 432 433 id := props[model.SYSTEM_DIAGNOSTIC_ID] 434 if len(id) == 0 { 435 id = model.NewId() 436 systemId := &model.System{Name: model.SYSTEM_DIAGNOSTIC_ID, Value: id} 437 <-a.Srv.Store.System().Save(systemId) 438 } 439 440 a.diagnosticId = id 441 } 442 } 443 444 // Go creates a goroutine, but maintains a record of it to ensure that execution completes before 445 // the app is destroyed. 446 func (a *App) Go(f func()) { 447 atomic.AddInt32(&a.goroutineCount, 1) 448 449 go func() { 450 f() 451 452 atomic.AddInt32(&a.goroutineCount, -1) 453 select { 454 case a.goroutineExitSignal <- struct{}{}: 455 default: 456 } 457 }() 458 } 459 460 // WaitForGoroutines blocks until all goroutines created by App.Go exit. 461 func (a *App) WaitForGoroutines() { 462 for atomic.LoadInt32(&a.goroutineCount) != 0 { 463 <-a.goroutineExitSignal 464 } 465 } 466 467 func (a *App) HTMLTemplates() *template.Template { 468 if a.htmlTemplateWatcher != nil { 469 return a.htmlTemplateWatcher.Templates() 470 } 471 472 return nil 473 } 474 475 func (a *App) HTTPClient(trustURLs bool) *http.Client { 476 insecure := a.Config().ServiceSettings.EnableInsecureOutgoingConnections != nil && *a.Config().ServiceSettings.EnableInsecureOutgoingConnections 477 478 if trustURLs { 479 return utils.NewHTTPClient(insecure, nil, nil) 480 } 481 482 allowHost := func(host string) bool { 483 if a.Config().ServiceSettings.AllowedUntrustedInternalConnections == nil { 484 return false 485 } 486 for _, allowed := range strings.Fields(*a.Config().ServiceSettings.AllowedUntrustedInternalConnections) { 487 if host == allowed { 488 return true 489 } 490 } 491 return false 492 } 493 494 allowIP := func(ip net.IP) bool { 495 reservedIP := utils.IsReservedIP(ip) 496 ownIP, err := utils.IsOwnIP(ip) 497 498 // If there is an error getting the self-assigned IPs, default to the secure option 499 if err != nil { 500 return false 501 } 502 503 // If it's not a reserved IP and it's not self-assigned IP, accept the IP 504 if !reservedIP && !ownIP { 505 return true 506 } 507 508 if a.Config().ServiceSettings.AllowedUntrustedInternalConnections == nil { 509 return false 510 } 511 512 // In the case it's the self-assigned IP, enforce that it needs to be explicitly added to the AllowedUntrustedInternalConnections 513 for _, allowed := range strings.Fields(*a.Config().ServiceSettings.AllowedUntrustedInternalConnections) { 514 if _, ipRange, err := net.ParseCIDR(allowed); err == nil && ipRange.Contains(ip) { 515 return true 516 } 517 } 518 return false 519 } 520 521 return utils.NewHTTPClient(insecure, allowHost, allowIP) 522 } 523 524 func (a *App) Handle404(w http.ResponseWriter, r *http.Request) { 525 err := model.NewAppError("Handle404", "api.context.404.app_error", nil, "", http.StatusNotFound) 526 527 mlog.Debug(fmt.Sprintf("%v: code=404 ip=%v", r.URL.Path, utils.GetIpAddress(r))) 528 529 utils.RenderWebAppError(w, r, err, a.AsymmetricSigningKey()) 530 } 531 532 // This function migrates the default built in roles from code/config to the database. 533 func (a *App) DoAdvancedPermissionsMigration() { 534 // If the migration is already marked as completed, don't do it again. 535 if result := <-a.Srv.Store.System().GetByName(ADVANCED_PERMISSIONS_MIGRATION_KEY); result.Err == nil { 536 return 537 } 538 539 mlog.Info("Migrating roles to database.") 540 roles := model.MakeDefaultRoles() 541 roles = utils.SetRolePermissionsFromConfig(roles, a.Config(), a.License() != nil) 542 543 allSucceeded := true 544 545 for _, role := range roles { 546 if result := <-a.Srv.Store.Role().Save(role); result.Err != nil { 547 // If this failed for reasons other than the role already existing, don't mark the migration as done. 548 if result2 := <-a.Srv.Store.Role().GetByName(role.Name); result2.Err != nil { 549 mlog.Critical("Failed to migrate role to database.") 550 mlog.Critical(fmt.Sprint(result.Err)) 551 allSucceeded = false 552 } else { 553 // If the role already existed, check it is the same and update if not. 554 fetchedRole := result.Data.(*model.Role) 555 if !reflect.DeepEqual(fetchedRole.Permissions, role.Permissions) || 556 fetchedRole.DisplayName != role.DisplayName || 557 fetchedRole.Description != role.Description || 558 fetchedRole.SchemeManaged != role.SchemeManaged { 559 role.Id = fetchedRole.Id 560 if result := <-a.Srv.Store.Role().Save(role); result.Err != nil { 561 // Role is not the same, but failed to update. 562 mlog.Critical("Failed to migrate role to database.") 563 mlog.Critical(fmt.Sprint(result.Err)) 564 allSucceeded = false 565 } 566 } 567 } 568 } 569 } 570 571 if !allSucceeded { 572 return 573 } 574 575 config := a.Config() 576 if *config.ServiceSettings.AllowEditPost == model.ALLOW_EDIT_POST_ALWAYS { 577 *config.ServiceSettings.PostEditTimeLimit = -1 578 if err := a.SaveConfig(config, true); err != nil { 579 mlog.Error("Failed to update config in Advanced Permissions Phase 1 Migration.", mlog.String("error", err.Error())) 580 } 581 } 582 583 system := model.System{ 584 Name: ADVANCED_PERMISSIONS_MIGRATION_KEY, 585 Value: "true", 586 } 587 588 if result := <-a.Srv.Store.System().Save(&system); result.Err != nil { 589 mlog.Critical("Failed to mark advanced permissions migration as completed.") 590 mlog.Critical(fmt.Sprint(result.Err)) 591 } 592 }