github.com/psyb0t/mattermost-server@v4.6.1-0.20180125161845-5503a1351abf+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  	"html/template"
     8  	"net"
     9  	"net/http"
    10  	"strings"
    11  	"sync"
    12  	"sync/atomic"
    13  
    14  	l4g "github.com/alecthomas/log4go"
    15  	"github.com/gorilla/mux"
    16  	"github.com/pkg/errors"
    17  
    18  	"github.com/mattermost/mattermost-server/einterfaces"
    19  	ejobs "github.com/mattermost/mattermost-server/einterfaces/jobs"
    20  	"github.com/mattermost/mattermost-server/jobs"
    21  	"github.com/mattermost/mattermost-server/model"
    22  	"github.com/mattermost/mattermost-server/plugin/pluginenv"
    23  	"github.com/mattermost/mattermost-server/store"
    24  	"github.com/mattermost/mattermost-server/store/sqlstore"
    25  	"github.com/mattermost/mattermost-server/utils"
    26  )
    27  
    28  type App struct {
    29  	goroutineCount      int32
    30  	goroutineExitSignal chan struct{}
    31  
    32  	Srv *Server
    33  
    34  	PluginEnv              *pluginenv.Environment
    35  	PluginConfigListenerId string
    36  
    37  	EmailBatching *EmailBatchingJob
    38  
    39  	Hubs                        []*Hub
    40  	HubsStopCheckingForDeadlock chan bool
    41  
    42  	Jobs *jobs.JobServer
    43  
    44  	AccountMigration einterfaces.AccountMigrationInterface
    45  	Brand            einterfaces.BrandInterface
    46  	Cluster          einterfaces.ClusterInterface
    47  	Compliance       einterfaces.ComplianceInterface
    48  	DataRetention    einterfaces.DataRetentionInterface
    49  	Elasticsearch    einterfaces.ElasticsearchInterface
    50  	Emoji            einterfaces.EmojiInterface
    51  	Ldap             einterfaces.LdapInterface
    52  	MessageExport    einterfaces.MessageExportInterface
    53  	Metrics          einterfaces.MetricsInterface
    54  	Mfa              einterfaces.MfaInterface
    55  	Saml             einterfaces.SamlInterface
    56  
    57  	config          atomic.Value
    58  	configFile      string
    59  	configListeners map[string]func(*model.Config, *model.Config)
    60  
    61  	newStore func() store.Store
    62  
    63  	htmlTemplateWatcher *utils.HTMLTemplateWatcher
    64  	sessionCache        *utils.Cache
    65  	roles               map[string]*model.Role
    66  	configListenerId    string
    67  	licenseListenerId   string
    68  	disableConfigWatch  bool
    69  	configWatcher       *utils.ConfigWatcher
    70  
    71  	pluginCommands     []*PluginCommand
    72  	pluginCommandsLock sync.RWMutex
    73  
    74  	clientConfig     map[string]string
    75  	clientConfigHash string
    76  	diagnosticId     string
    77  }
    78  
    79  var appCount = 0
    80  
    81  // New creates a new App. You must call Shutdown when you're done with it.
    82  // XXX: For now, only one at a time is allowed as some resources are still shared.
    83  func New(options ...Option) (*App, error) {
    84  	appCount++
    85  	if appCount > 1 {
    86  		panic("Only one App should exist at a time. Did you forget to call Shutdown()?")
    87  	}
    88  
    89  	app := &App{
    90  		goroutineExitSignal: make(chan struct{}, 1),
    91  		Srv: &Server{
    92  			Router: mux.NewRouter(),
    93  		},
    94  		sessionCache:    utils.NewLru(model.SESSION_CACHE_SIZE),
    95  		configFile:      "config.json",
    96  		configListeners: make(map[string]func(*model.Config, *model.Config)),
    97  		clientConfig:    make(map[string]string),
    98  	}
    99  
   100  	for _, option := range options {
   101  		option(app)
   102  	}
   103  
   104  	if utils.T == nil {
   105  		if err := utils.TranslationsPreInit(); err != nil {
   106  			return nil, errors.Wrapf(err, "unable to load Mattermost translation files")
   107  		}
   108  	}
   109  	model.AppErrorInit(utils.T)
   110  	if err := app.LoadConfig(app.configFile); err != nil {
   111  		return nil, err
   112  	}
   113  	app.EnableConfigWatch()
   114  	if err := utils.InitTranslations(app.Config().LocalizationSettings); err != nil {
   115  		return nil, errors.Wrapf(err, "unable to load Mattermost translation files")
   116  	}
   117  
   118  	app.configListenerId = app.AddConfigListener(func(_, _ *model.Config) {
   119  		app.configOrLicenseListener()
   120  	})
   121  	app.licenseListenerId = utils.AddLicenseListener(app.configOrLicenseListener)
   122  	app.regenerateClientConfig()
   123  	app.SetDefaultRolesBasedOnConfig()
   124  
   125  	l4g.Info(utils.T("api.server.new_server.init.info"))
   126  
   127  	app.initEnterprise()
   128  
   129  	if app.newStore == nil {
   130  		app.newStore = func() store.Store {
   131  			return store.NewLayeredStore(sqlstore.NewSqlSupplier(app.Config().SqlSettings, app.Metrics), app.Metrics, app.Cluster)
   132  		}
   133  	}
   134  
   135  	if htmlTemplateWatcher, err := utils.NewHTMLTemplateWatcher("templates"); err != nil {
   136  		l4g.Error(utils.T("api.api.init.parsing_templates.error"), err)
   137  	} else {
   138  		app.htmlTemplateWatcher = htmlTemplateWatcher
   139  	}
   140  
   141  	app.Srv.Store = app.newStore()
   142  	app.initJobs()
   143  
   144  	app.initBuiltInPlugins()
   145  	app.Srv.Router.HandleFunc("/plugins/{plugin_id:[A-Za-z0-9\\_\\-\\.]+}", app.ServePluginRequest)
   146  	app.Srv.Router.HandleFunc("/plugins/{plugin_id:[A-Za-z0-9\\_\\-\\.]+}/{anything:.*}", app.ServePluginRequest)
   147  
   148  	app.Srv.Router.NotFoundHandler = http.HandlerFunc(app.Handle404)
   149  
   150  	app.Srv.WebSocketRouter = &WebSocketRouter{
   151  		app:      app,
   152  		handlers: make(map[string]webSocketHandler),
   153  	}
   154  
   155  	return app, nil
   156  }
   157  
   158  func (a *App) configOrLicenseListener() {
   159  	a.regenerateClientConfig()
   160  	a.SetDefaultRolesBasedOnConfig()
   161  }
   162  
   163  func (a *App) Shutdown() {
   164  	appCount--
   165  
   166  	l4g.Info(utils.T("api.server.stop_server.stopping.info"))
   167  
   168  	a.StopServer()
   169  	a.HubStop()
   170  
   171  	a.ShutDownPlugins()
   172  	a.WaitForGoroutines()
   173  
   174  	a.Srv.Store.Close()
   175  	a.Srv = nil
   176  
   177  	if a.htmlTemplateWatcher != nil {
   178  		a.htmlTemplateWatcher.Close()
   179  	}
   180  
   181  	a.RemoveConfigListener(a.configListenerId)
   182  	utils.RemoveLicenseListener(a.licenseListenerId)
   183  	l4g.Info(utils.T("api.server.stop_server.stopped.info"))
   184  
   185  	a.DisableConfigWatch()
   186  }
   187  
   188  var accountMigrationInterface func(*App) einterfaces.AccountMigrationInterface
   189  
   190  func RegisterAccountMigrationInterface(f func(*App) einterfaces.AccountMigrationInterface) {
   191  	accountMigrationInterface = f
   192  }
   193  
   194  var brandInterface func(*App) einterfaces.BrandInterface
   195  
   196  func RegisterBrandInterface(f func(*App) einterfaces.BrandInterface) {
   197  	brandInterface = f
   198  }
   199  
   200  var clusterInterface func(*App) einterfaces.ClusterInterface
   201  
   202  func RegisterClusterInterface(f func(*App) einterfaces.ClusterInterface) {
   203  	clusterInterface = f
   204  }
   205  
   206  var complianceInterface func(*App) einterfaces.ComplianceInterface
   207  
   208  func RegisterComplianceInterface(f func(*App) einterfaces.ComplianceInterface) {
   209  	complianceInterface = f
   210  }
   211  
   212  var dataRetentionInterface func(*App) einterfaces.DataRetentionInterface
   213  
   214  func RegisterDataRetentionInterface(f func(*App) einterfaces.DataRetentionInterface) {
   215  	dataRetentionInterface = f
   216  }
   217  
   218  var elasticsearchInterface func(*App) einterfaces.ElasticsearchInterface
   219  
   220  func RegisterElasticsearchInterface(f func(*App) einterfaces.ElasticsearchInterface) {
   221  	elasticsearchInterface = f
   222  }
   223  
   224  var emojiInterface func(*App) einterfaces.EmojiInterface
   225  
   226  func RegisterEmojiInterface(f func(*App) einterfaces.EmojiInterface) {
   227  	emojiInterface = f
   228  }
   229  
   230  var jobsDataRetentionJobInterface func(*App) ejobs.DataRetentionJobInterface
   231  
   232  func RegisterJobsDataRetentionJobInterface(f func(*App) ejobs.DataRetentionJobInterface) {
   233  	jobsDataRetentionJobInterface = f
   234  }
   235  
   236  var jobsMessageExportJobInterface func(*App) ejobs.MessageExportJobInterface
   237  
   238  func RegisterJobsMessageExportJobInterface(f func(*App) ejobs.MessageExportJobInterface) {
   239  	jobsMessageExportJobInterface = f
   240  }
   241  
   242  var jobsElasticsearchAggregatorInterface func(*App) ejobs.ElasticsearchAggregatorInterface
   243  
   244  func RegisterJobsElasticsearchAggregatorInterface(f func(*App) ejobs.ElasticsearchAggregatorInterface) {
   245  	jobsElasticsearchAggregatorInterface = f
   246  }
   247  
   248  var jobsElasticsearchIndexerInterface func(*App) ejobs.ElasticsearchIndexerInterface
   249  
   250  func RegisterJobsElasticsearchIndexerInterface(f func(*App) ejobs.ElasticsearchIndexerInterface) {
   251  	jobsElasticsearchIndexerInterface = f
   252  }
   253  
   254  var jobsLdapSyncInterface func(*App) ejobs.LdapSyncInterface
   255  
   256  func RegisterJobsLdapSyncInterface(f func(*App) ejobs.LdapSyncInterface) {
   257  	jobsLdapSyncInterface = f
   258  }
   259  
   260  var ldapInterface func(*App) einterfaces.LdapInterface
   261  
   262  func RegisterLdapInterface(f func(*App) einterfaces.LdapInterface) {
   263  	ldapInterface = f
   264  }
   265  
   266  var messageExportInterface func(*App) einterfaces.MessageExportInterface
   267  
   268  func RegisterMessageExportInterface(f func(*App) einterfaces.MessageExportInterface) {
   269  	messageExportInterface = f
   270  }
   271  
   272  var metricsInterface func(*App) einterfaces.MetricsInterface
   273  
   274  func RegisterMetricsInterface(f func(*App) einterfaces.MetricsInterface) {
   275  	metricsInterface = f
   276  }
   277  
   278  var mfaInterface func(*App) einterfaces.MfaInterface
   279  
   280  func RegisterMfaInterface(f func(*App) einterfaces.MfaInterface) {
   281  	mfaInterface = f
   282  }
   283  
   284  var samlInterface func(*App) einterfaces.SamlInterface
   285  
   286  func RegisterSamlInterface(f func(*App) einterfaces.SamlInterface) {
   287  	samlInterface = f
   288  }
   289  
   290  func (a *App) initEnterprise() {
   291  	if accountMigrationInterface != nil {
   292  		a.AccountMigration = accountMigrationInterface(a)
   293  	}
   294  	if brandInterface != nil {
   295  		a.Brand = brandInterface(a)
   296  	}
   297  	if clusterInterface != nil {
   298  		a.Cluster = clusterInterface(a)
   299  	}
   300  	if complianceInterface != nil {
   301  		a.Compliance = complianceInterface(a)
   302  	}
   303  	if elasticsearchInterface != nil {
   304  		a.Elasticsearch = elasticsearchInterface(a)
   305  	}
   306  	if emojiInterface != nil {
   307  		a.Emoji = emojiInterface(a)
   308  	}
   309  	if ldapInterface != nil {
   310  		a.Ldap = ldapInterface(a)
   311  		a.AddConfigListener(func(_, cfg *model.Config) {
   312  			if err := utils.ValidateLdapFilter(cfg, a.Ldap); err != nil {
   313  				panic(utils.T(err.Id))
   314  			}
   315  		})
   316  	}
   317  	if messageExportInterface != nil {
   318  		a.MessageExport = messageExportInterface(a)
   319  	}
   320  	if metricsInterface != nil {
   321  		a.Metrics = metricsInterface(a)
   322  	}
   323  	if mfaInterface != nil {
   324  		a.Mfa = mfaInterface(a)
   325  	}
   326  	if samlInterface != nil {
   327  		a.Saml = samlInterface(a)
   328  		a.AddConfigListener(func(_, cfg *model.Config) {
   329  			a.Saml.ConfigureSP()
   330  		})
   331  	}
   332  	if dataRetentionInterface != nil {
   333  		a.DataRetention = dataRetentionInterface(a)
   334  	}
   335  }
   336  
   337  func (a *App) initJobs() {
   338  	a.Jobs = jobs.NewJobServer(a, a.Srv.Store)
   339  	if jobsDataRetentionJobInterface != nil {
   340  		a.Jobs.DataRetentionJob = jobsDataRetentionJobInterface(a)
   341  	}
   342  	if jobsMessageExportJobInterface != nil {
   343  		a.Jobs.MessageExportJob = jobsMessageExportJobInterface(a)
   344  	}
   345  	if jobsElasticsearchAggregatorInterface != nil {
   346  		a.Jobs.ElasticsearchAggregator = jobsElasticsearchAggregatorInterface(a)
   347  	}
   348  	if jobsElasticsearchIndexerInterface != nil {
   349  		a.Jobs.ElasticsearchIndexer = jobsElasticsearchIndexerInterface(a)
   350  	}
   351  	if jobsLdapSyncInterface != nil {
   352  		a.Jobs.LdapSync = jobsLdapSyncInterface(a)
   353  	}
   354  }
   355  
   356  func (a *App) DiagnosticId() string {
   357  	return a.diagnosticId
   358  }
   359  
   360  func (a *App) SetDiagnosticId(id string) {
   361  	a.diagnosticId = id
   362  }
   363  
   364  func (a *App) EnsureDiagnosticId() {
   365  	if a.diagnosticId != "" {
   366  		return
   367  	}
   368  	if result := <-a.Srv.Store.System().Get(); result.Err == nil {
   369  		props := result.Data.(model.StringMap)
   370  
   371  		id := props[model.SYSTEM_DIAGNOSTIC_ID]
   372  		if len(id) == 0 {
   373  			id = model.NewId()
   374  			systemId := &model.System{Name: model.SYSTEM_DIAGNOSTIC_ID, Value: id}
   375  			<-a.Srv.Store.System().Save(systemId)
   376  		}
   377  
   378  		a.diagnosticId = id
   379  	}
   380  }
   381  
   382  // Go creates a goroutine, but maintains a record of it to ensure that execution completes before
   383  // the app is destroyed.
   384  func (a *App) Go(f func()) {
   385  	atomic.AddInt32(&a.goroutineCount, 1)
   386  
   387  	go func() {
   388  		f()
   389  
   390  		atomic.AddInt32(&a.goroutineCount, -1)
   391  		select {
   392  		case a.goroutineExitSignal <- struct{}{}:
   393  		default:
   394  		}
   395  	}()
   396  }
   397  
   398  // WaitForGoroutines blocks until all goroutines created by App.Go exit.
   399  func (a *App) WaitForGoroutines() {
   400  	for atomic.LoadInt32(&a.goroutineCount) != 0 {
   401  		<-a.goroutineExitSignal
   402  	}
   403  }
   404  
   405  func (a *App) HTMLTemplates() *template.Template {
   406  	return a.htmlTemplateWatcher.Templates()
   407  }
   408  
   409  func (a *App) HTTPClient(trustURLs bool) *http.Client {
   410  	insecure := a.Config().ServiceSettings.EnableInsecureOutgoingConnections != nil && *a.Config().ServiceSettings.EnableInsecureOutgoingConnections
   411  
   412  	if trustURLs {
   413  		return utils.NewHTTPClient(insecure, nil, nil)
   414  	}
   415  
   416  	allowHost := func(host string) bool {
   417  		if a.Config().ServiceSettings.AllowedUntrustedInternalConnections == nil {
   418  			return false
   419  		}
   420  		for _, allowed := range strings.Fields(*a.Config().ServiceSettings.AllowedUntrustedInternalConnections) {
   421  			if host == allowed {
   422  				return true
   423  			}
   424  		}
   425  		return false
   426  	}
   427  
   428  	allowIP := func(ip net.IP) bool {
   429  		if !utils.IsReservedIP(ip) {
   430  			return true
   431  		}
   432  		if a.Config().ServiceSettings.AllowedUntrustedInternalConnections == nil {
   433  			return false
   434  		}
   435  		for _, allowed := range strings.Fields(*a.Config().ServiceSettings.AllowedUntrustedInternalConnections) {
   436  			if _, ipRange, err := net.ParseCIDR(allowed); err == nil && ipRange.Contains(ip) {
   437  				return true
   438  			}
   439  		}
   440  		return false
   441  	}
   442  
   443  	return utils.NewHTTPClient(insecure, allowHost, allowIP)
   444  }
   445  
   446  func (a *App) Handle404(w http.ResponseWriter, r *http.Request) {
   447  	err := model.NewAppError("Handle404", "api.context.404.app_error", nil, "", http.StatusNotFound)
   448  
   449  	l4g.Debug("%v: code=404 ip=%v", r.URL.Path, utils.GetIpAddress(r))
   450  
   451  	utils.RenderWebError(err, w, r)
   452  }