github.com/jlevesy/mattermost-server@v5.3.2-0.20181003190404-7468f35cb0c8+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/http"
    11  	"path"
    12  	"reflect"
    13  	"strconv"
    14  	"sync"
    15  	"sync/atomic"
    16  
    17  	"github.com/gorilla/mux"
    18  	"github.com/pkg/errors"
    19  	"github.com/throttled/throttled"
    20  
    21  	"github.com/mattermost/mattermost-server/einterfaces"
    22  	ejobs "github.com/mattermost/mattermost-server/einterfaces/jobs"
    23  	"github.com/mattermost/mattermost-server/jobs"
    24  	tjobs "github.com/mattermost/mattermost-server/jobs/interfaces"
    25  	"github.com/mattermost/mattermost-server/mlog"
    26  	"github.com/mattermost/mattermost-server/model"
    27  	"github.com/mattermost/mattermost-server/plugin"
    28  	"github.com/mattermost/mattermost-server/services/httpservice"
    29  	"github.com/mattermost/mattermost-server/store"
    30  	"github.com/mattermost/mattermost-server/store/sqlstore"
    31  	"github.com/mattermost/mattermost-server/utils"
    32  )
    33  
    34  const ADVANCED_PERMISSIONS_MIGRATION_KEY = "AdvancedPermissionsMigrationComplete"
    35  const EMOJIS_PERMISSIONS_MIGRATION_KEY = "EmojisPermissionsMigrationComplete"
    36  
    37  type App struct {
    38  	goroutineCount      int32
    39  	goroutineExitSignal chan struct{}
    40  
    41  	Srv *Server
    42  
    43  	Log *mlog.Logger
    44  
    45  	Plugins                *plugin.Environment
    46  	PluginConfigListenerId string
    47  
    48  	EmailBatching    *EmailBatchingJob
    49  	EmailRateLimiter *throttled.GCRARateLimiter
    50  
    51  	Hubs                        []*Hub
    52  	HubsStopCheckingForDeadlock chan bool
    53  
    54  	PushNotificationsHub PushNotificationsHub
    55  
    56  	Jobs *jobs.JobServer
    57  
    58  	AccountMigration einterfaces.AccountMigrationInterface
    59  	Cluster          einterfaces.ClusterInterface
    60  	Compliance       einterfaces.ComplianceInterface
    61  	DataRetention    einterfaces.DataRetentionInterface
    62  	Elasticsearch    einterfaces.ElasticsearchInterface
    63  	Ldap             einterfaces.LdapInterface
    64  	MessageExport    einterfaces.MessageExportInterface
    65  	Metrics          einterfaces.MetricsInterface
    66  	Mfa              einterfaces.MfaInterface
    67  	Saml             einterfaces.SamlInterface
    68  
    69  	config                 atomic.Value
    70  	envConfig              map[string]interface{}
    71  	configFile             string
    72  	configListeners        map[string]func(*model.Config, *model.Config)
    73  	clusterLeaderListeners sync.Map
    74  
    75  	licenseValue       atomic.Value
    76  	clientLicenseValue atomic.Value
    77  	licenseListeners   map[string]func()
    78  
    79  	timezones atomic.Value
    80  
    81  	siteURL string
    82  
    83  	newStore func() store.Store
    84  
    85  	htmlTemplateWatcher     *utils.HTMLTemplateWatcher
    86  	sessionCache            *utils.Cache
    87  	configListenerId        string
    88  	licenseListenerId       string
    89  	logListenerId           string
    90  	clusterLeaderListenerId string
    91  	disableConfigWatch      bool
    92  	configWatcher           *utils.ConfigWatcher
    93  	asymmetricSigningKey    *ecdsa.PrivateKey
    94  
    95  	pluginCommands     []*PluginCommand
    96  	pluginCommandsLock sync.RWMutex
    97  
    98  	clientConfig        map[string]string
    99  	clientConfigHash    string
   100  	limitedClientConfig map[string]string
   101  	diagnosticId        string
   102  
   103  	phase2PermissionsMigrationComplete bool
   104  
   105  	HTTPService httpservice.HTTPService
   106  }
   107  
   108  var appCount = 0
   109  
   110  // New creates a new App. You must call Shutdown when you're done with it.
   111  // XXX: For now, only one at a time is allowed as some resources are still shared.
   112  func New(options ...Option) (outApp *App, outErr error) {
   113  	appCount++
   114  	if appCount > 1 {
   115  		panic("Only one App should exist at a time. Did you forget to call Shutdown()?")
   116  	}
   117  
   118  	rootRouter := mux.NewRouter()
   119  
   120  	app := &App{
   121  		goroutineExitSignal: make(chan struct{}, 1),
   122  		Srv: &Server{
   123  			RootRouter: rootRouter,
   124  		},
   125  		sessionCache:     utils.NewLru(model.SESSION_CACHE_SIZE),
   126  		configFile:       "config.json",
   127  		configListeners:  make(map[string]func(*model.Config, *model.Config)),
   128  		clientConfig:     make(map[string]string),
   129  		licenseListeners: map[string]func(){},
   130  	}
   131  
   132  	app.HTTPService = httpservice.MakeHTTPService(app)
   133  
   134  	app.CreatePushNotificationsHub()
   135  	app.StartPushNotificationsHubWorkers()
   136  
   137  	defer func() {
   138  		if outErr != nil {
   139  			app.Shutdown()
   140  		}
   141  	}()
   142  
   143  	for _, option := range options {
   144  		option(app)
   145  	}
   146  
   147  	if utils.T == nil {
   148  		if err := utils.TranslationsPreInit(); err != nil {
   149  			return nil, errors.Wrapf(err, "unable to load Mattermost translation files")
   150  		}
   151  	}
   152  	model.AppErrorInit(utils.T)
   153  
   154  	if err := app.LoadConfig(app.configFile); err != nil {
   155  		return nil, err
   156  	}
   157  
   158  	// Initalize logging
   159  	app.Log = mlog.NewLogger(utils.MloggerConfigFromLoggerConfig(&app.Config().LogSettings))
   160  
   161  	// Redirect default golang logger to this logger
   162  	mlog.RedirectStdLog(app.Log)
   163  
   164  	// Use this app logger as the global logger (eventually remove all instances of global logging)
   165  	mlog.InitGlobalLogger(app.Log)
   166  
   167  	app.logListenerId = app.AddConfigListener(func(_, after *model.Config) {
   168  		app.Log.ChangeLevels(utils.MloggerConfigFromLoggerConfig(&after.LogSettings))
   169  	})
   170  
   171  	app.EnableConfigWatch()
   172  
   173  	app.LoadTimezones()
   174  
   175  	if err := utils.InitTranslations(app.Config().LocalizationSettings); err != nil {
   176  		return nil, errors.Wrapf(err, "unable to load Mattermost translation files")
   177  	}
   178  
   179  	app.configListenerId = app.AddConfigListener(func(_, _ *model.Config) {
   180  		app.configOrLicenseListener()
   181  
   182  		message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CONFIG_CHANGED, "", "", "", nil)
   183  
   184  		message.Add("config", app.ClientConfigWithComputed())
   185  		app.Go(func() {
   186  			app.Publish(message)
   187  		})
   188  	})
   189  	app.licenseListenerId = app.AddLicenseListener(func() {
   190  		app.configOrLicenseListener()
   191  
   192  		message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_LICENSE_CHANGED, "", "", "", nil)
   193  		message.Add("license", app.GetSanitizedClientLicense())
   194  		app.Go(func() {
   195  			app.Publish(message)
   196  		})
   197  
   198  	})
   199  
   200  	if err := app.SetupInviteEmailRateLimiting(); err != nil {
   201  		return nil, err
   202  	}
   203  
   204  	mlog.Info("Server is initializing...")
   205  
   206  	app.initEnterprise()
   207  
   208  	if app.newStore == nil {
   209  		app.newStore = func() store.Store {
   210  			return store.NewLayeredStore(sqlstore.NewSqlSupplier(app.Config().SqlSettings, app.Metrics), app.Metrics, app.Cluster)
   211  		}
   212  	}
   213  
   214  	if htmlTemplateWatcher, err := utils.NewHTMLTemplateWatcher("templates"); err != nil {
   215  		mlog.Error(fmt.Sprintf("Failed to parse server templates %v", err))
   216  	} else {
   217  		app.htmlTemplateWatcher = htmlTemplateWatcher
   218  	}
   219  
   220  	app.Srv.Store = app.newStore()
   221  
   222  	app.AddConfigListener(func(_, current *model.Config) {
   223  		if current.SqlSettings.EnablePublicChannelsMaterialization != nil && !*current.SqlSettings.EnablePublicChannelsMaterialization {
   224  			app.Srv.Store.Channel().DisableExperimentalPublicChannelsMaterialization()
   225  		} else {
   226  			app.Srv.Store.Channel().EnableExperimentalPublicChannelsMaterialization()
   227  		}
   228  	})
   229  
   230  	if err := app.ensureAsymmetricSigningKey(); err != nil {
   231  		return nil, errors.Wrapf(err, "unable to ensure asymmetric signing key")
   232  	}
   233  
   234  	if err := app.ensureInstallationDate(); err != nil {
   235  		return nil, errors.Wrapf(err, "unable to ensure installation date")
   236  	}
   237  
   238  	app.EnsureDiagnosticId()
   239  	app.regenerateClientConfig()
   240  
   241  	app.initJobs()
   242  	app.AddLicenseListener(func() {
   243  		app.initJobs()
   244  	})
   245  
   246  	app.clusterLeaderListenerId = app.AddClusterLeaderChangedListener(func() {
   247  		mlog.Info("Cluster leader changed. Determining if job schedulers should be running:", mlog.Bool("isLeader", app.IsLeader()))
   248  		app.Jobs.Schedulers.HandleClusterLeaderChange(app.IsLeader())
   249  	})
   250  
   251  	subpath, err := utils.GetSubpathFromConfig(app.Config())
   252  	if err != nil {
   253  		return nil, errors.Wrap(err, "failed to parse SiteURL subpath")
   254  	}
   255  	app.Srv.Router = app.Srv.RootRouter.PathPrefix(subpath).Subrouter()
   256  	app.Srv.Router.HandleFunc("/plugins/{plugin_id:[A-Za-z0-9\\_\\-\\.]+}", app.ServePluginRequest)
   257  	app.Srv.Router.HandleFunc("/plugins/{plugin_id:[A-Za-z0-9\\_\\-\\.]+}/{anything:.*}", app.ServePluginRequest)
   258  
   259  	// If configured with a subpath, redirect 404s at the root back into the subpath.
   260  	if subpath != "/" {
   261  		app.Srv.RootRouter.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   262  			r.URL.Path = path.Join(subpath, r.URL.Path)
   263  			http.Redirect(w, r, r.URL.String(), http.StatusFound)
   264  		})
   265  	}
   266  	app.Srv.Router.NotFoundHandler = http.HandlerFunc(app.Handle404)
   267  
   268  	app.Srv.WebSocketRouter = &WebSocketRouter{
   269  		app:      app,
   270  		handlers: make(map[string]webSocketHandler),
   271  	}
   272  
   273  	return app, nil
   274  }
   275  
   276  func (a *App) configOrLicenseListener() {
   277  	a.regenerateClientConfig()
   278  }
   279  
   280  func (a *App) Shutdown() {
   281  	appCount--
   282  
   283  	mlog.Info("Stopping Server...")
   284  
   285  	a.StopServer()
   286  	a.HubStop()
   287  	a.StopPushNotificationsHubWorkers()
   288  
   289  	a.ShutDownPlugins()
   290  	a.WaitForGoroutines()
   291  
   292  	if a.Srv.Store != nil {
   293  		a.Srv.Store.Close()
   294  	}
   295  	a.Srv = nil
   296  
   297  	if a.htmlTemplateWatcher != nil {
   298  		a.htmlTemplateWatcher.Close()
   299  	}
   300  
   301  	a.RemoveConfigListener(a.configListenerId)
   302  	a.RemoveLicenseListener(a.licenseListenerId)
   303  	a.RemoveConfigListener(a.logListenerId)
   304  	a.RemoveClusterLeaderChangedListener(a.clusterLeaderListenerId)
   305  	mlog.Info("Server stopped")
   306  
   307  	a.DisableConfigWatch()
   308  
   309  	a.HTTPService.Close()
   310  }
   311  
   312  var accountMigrationInterface func(*App) einterfaces.AccountMigrationInterface
   313  
   314  func RegisterAccountMigrationInterface(f func(*App) einterfaces.AccountMigrationInterface) {
   315  	accountMigrationInterface = f
   316  }
   317  
   318  var clusterInterface func(*App) einterfaces.ClusterInterface
   319  
   320  func RegisterClusterInterface(f func(*App) einterfaces.ClusterInterface) {
   321  	clusterInterface = f
   322  }
   323  
   324  var complianceInterface func(*App) einterfaces.ComplianceInterface
   325  
   326  func RegisterComplianceInterface(f func(*App) einterfaces.ComplianceInterface) {
   327  	complianceInterface = f
   328  }
   329  
   330  var dataRetentionInterface func(*App) einterfaces.DataRetentionInterface
   331  
   332  func RegisterDataRetentionInterface(f func(*App) einterfaces.DataRetentionInterface) {
   333  	dataRetentionInterface = f
   334  }
   335  
   336  var elasticsearchInterface func(*App) einterfaces.ElasticsearchInterface
   337  
   338  func RegisterElasticsearchInterface(f func(*App) einterfaces.ElasticsearchInterface) {
   339  	elasticsearchInterface = f
   340  }
   341  
   342  var jobsDataRetentionJobInterface func(*App) ejobs.DataRetentionJobInterface
   343  
   344  func RegisterJobsDataRetentionJobInterface(f func(*App) ejobs.DataRetentionJobInterface) {
   345  	jobsDataRetentionJobInterface = f
   346  }
   347  
   348  var jobsMessageExportJobInterface func(*App) ejobs.MessageExportJobInterface
   349  
   350  func RegisterJobsMessageExportJobInterface(f func(*App) ejobs.MessageExportJobInterface) {
   351  	jobsMessageExportJobInterface = f
   352  }
   353  
   354  var jobsElasticsearchAggregatorInterface func(*App) ejobs.ElasticsearchAggregatorInterface
   355  
   356  func RegisterJobsElasticsearchAggregatorInterface(f func(*App) ejobs.ElasticsearchAggregatorInterface) {
   357  	jobsElasticsearchAggregatorInterface = f
   358  }
   359  
   360  var jobsElasticsearchIndexerInterface func(*App) ejobs.ElasticsearchIndexerInterface
   361  
   362  func RegisterJobsElasticsearchIndexerInterface(f func(*App) ejobs.ElasticsearchIndexerInterface) {
   363  	jobsElasticsearchIndexerInterface = f
   364  }
   365  
   366  var jobsLdapSyncInterface func(*App) ejobs.LdapSyncInterface
   367  
   368  func RegisterJobsLdapSyncInterface(f func(*App) ejobs.LdapSyncInterface) {
   369  	jobsLdapSyncInterface = f
   370  }
   371  
   372  var jobsMigrationsInterface func(*App) tjobs.MigrationsJobInterface
   373  
   374  func RegisterJobsMigrationsJobInterface(f func(*App) tjobs.MigrationsJobInterface) {
   375  	jobsMigrationsInterface = f
   376  }
   377  
   378  var ldapInterface func(*App) einterfaces.LdapInterface
   379  
   380  func RegisterLdapInterface(f func(*App) einterfaces.LdapInterface) {
   381  	ldapInterface = f
   382  }
   383  
   384  var messageExportInterface func(*App) einterfaces.MessageExportInterface
   385  
   386  func RegisterMessageExportInterface(f func(*App) einterfaces.MessageExportInterface) {
   387  	messageExportInterface = f
   388  }
   389  
   390  var metricsInterface func(*App) einterfaces.MetricsInterface
   391  
   392  func RegisterMetricsInterface(f func(*App) einterfaces.MetricsInterface) {
   393  	metricsInterface = f
   394  }
   395  
   396  var mfaInterface func(*App) einterfaces.MfaInterface
   397  
   398  func RegisterMfaInterface(f func(*App) einterfaces.MfaInterface) {
   399  	mfaInterface = f
   400  }
   401  
   402  var samlInterface func(*App) einterfaces.SamlInterface
   403  
   404  func RegisterSamlInterface(f func(*App) einterfaces.SamlInterface) {
   405  	samlInterface = f
   406  }
   407  
   408  func (a *App) initEnterprise() {
   409  	if accountMigrationInterface != nil {
   410  		a.AccountMigration = accountMigrationInterface(a)
   411  	}
   412  	if clusterInterface != nil {
   413  		a.Cluster = clusterInterface(a)
   414  	}
   415  	if complianceInterface != nil {
   416  		a.Compliance = complianceInterface(a)
   417  	}
   418  	if elasticsearchInterface != nil {
   419  		a.Elasticsearch = elasticsearchInterface(a)
   420  	}
   421  	if ldapInterface != nil {
   422  		a.Ldap = ldapInterface(a)
   423  		a.AddConfigListener(func(_, cfg *model.Config) {
   424  			if err := utils.ValidateLdapFilter(cfg, a.Ldap); err != nil {
   425  				panic(utils.T(err.Id))
   426  			}
   427  		})
   428  	}
   429  	if messageExportInterface != nil {
   430  		a.MessageExport = messageExportInterface(a)
   431  	}
   432  	if metricsInterface != nil {
   433  		a.Metrics = metricsInterface(a)
   434  	}
   435  	if mfaInterface != nil {
   436  		a.Mfa = mfaInterface(a)
   437  	}
   438  	if samlInterface != nil {
   439  		a.Saml = samlInterface(a)
   440  		a.AddConfigListener(func(_, cfg *model.Config) {
   441  			a.Saml.ConfigureSP()
   442  		})
   443  	}
   444  	if dataRetentionInterface != nil {
   445  		a.DataRetention = dataRetentionInterface(a)
   446  	}
   447  }
   448  
   449  func (a *App) initJobs() {
   450  	a.Jobs = jobs.NewJobServer(a, a.Srv.Store)
   451  	if jobsDataRetentionJobInterface != nil {
   452  		a.Jobs.DataRetentionJob = jobsDataRetentionJobInterface(a)
   453  	}
   454  	if jobsMessageExportJobInterface != nil {
   455  		a.Jobs.MessageExportJob = jobsMessageExportJobInterface(a)
   456  	}
   457  	if jobsElasticsearchAggregatorInterface != nil {
   458  		a.Jobs.ElasticsearchAggregator = jobsElasticsearchAggregatorInterface(a)
   459  	}
   460  	if jobsElasticsearchIndexerInterface != nil {
   461  		a.Jobs.ElasticsearchIndexer = jobsElasticsearchIndexerInterface(a)
   462  	}
   463  	if jobsLdapSyncInterface != nil {
   464  		a.Jobs.LdapSync = jobsLdapSyncInterface(a)
   465  	}
   466  	if jobsMigrationsInterface != nil {
   467  		a.Jobs.Migrations = jobsMigrationsInterface(a)
   468  	}
   469  	a.Jobs.Workers = a.Jobs.InitWorkers()
   470  	a.Jobs.Schedulers = a.Jobs.InitSchedulers()
   471  }
   472  
   473  func (a *App) DiagnosticId() string {
   474  	return a.diagnosticId
   475  }
   476  
   477  func (a *App) SetDiagnosticId(id string) {
   478  	a.diagnosticId = id
   479  }
   480  
   481  func (a *App) EnsureDiagnosticId() {
   482  	if a.diagnosticId != "" {
   483  		return
   484  	}
   485  	if result := <-a.Srv.Store.System().Get(); result.Err == nil {
   486  		props := result.Data.(model.StringMap)
   487  
   488  		id := props[model.SYSTEM_DIAGNOSTIC_ID]
   489  		if len(id) == 0 {
   490  			id = model.NewId()
   491  			systemId := &model.System{Name: model.SYSTEM_DIAGNOSTIC_ID, Value: id}
   492  			<-a.Srv.Store.System().Save(systemId)
   493  		}
   494  
   495  		a.diagnosticId = id
   496  	}
   497  }
   498  
   499  // Go creates a goroutine, but maintains a record of it to ensure that execution completes before
   500  // the app is destroyed.
   501  func (a *App) Go(f func()) {
   502  	atomic.AddInt32(&a.goroutineCount, 1)
   503  
   504  	go func() {
   505  		f()
   506  
   507  		atomic.AddInt32(&a.goroutineCount, -1)
   508  		select {
   509  		case a.goroutineExitSignal <- struct{}{}:
   510  		default:
   511  		}
   512  	}()
   513  }
   514  
   515  // WaitForGoroutines blocks until all goroutines created by App.Go exit.
   516  func (a *App) WaitForGoroutines() {
   517  	for atomic.LoadInt32(&a.goroutineCount) != 0 {
   518  		<-a.goroutineExitSignal
   519  	}
   520  }
   521  
   522  func (a *App) HTMLTemplates() *template.Template {
   523  	if a.htmlTemplateWatcher != nil {
   524  		return a.htmlTemplateWatcher.Templates()
   525  	}
   526  
   527  	return nil
   528  }
   529  
   530  func (a *App) Handle404(w http.ResponseWriter, r *http.Request) {
   531  	err := model.NewAppError("Handle404", "api.context.404.app_error", nil, "", http.StatusNotFound)
   532  
   533  	mlog.Debug(fmt.Sprintf("%v: code=404 ip=%v", r.URL.Path, utils.GetIpAddress(r)))
   534  
   535  	utils.RenderWebAppError(a.Config(), w, r, err, a.AsymmetricSigningKey())
   536  }
   537  
   538  // This function migrates the default built in roles from code/config to the database.
   539  func (a *App) DoAdvancedPermissionsMigration() {
   540  	// If the migration is already marked as completed, don't do it again.
   541  	if result := <-a.Srv.Store.System().GetByName(ADVANCED_PERMISSIONS_MIGRATION_KEY); result.Err == nil {
   542  		return
   543  	}
   544  
   545  	mlog.Info("Migrating roles to database.")
   546  	roles := model.MakeDefaultRoles()
   547  	roles = utils.SetRolePermissionsFromConfig(roles, a.Config(), a.License() != nil)
   548  
   549  	allSucceeded := true
   550  
   551  	for _, role := range roles {
   552  		if result := <-a.Srv.Store.Role().Save(role); result.Err != nil {
   553  			// If this failed for reasons other than the role already existing, don't mark the migration as done.
   554  			if result2 := <-a.Srv.Store.Role().GetByName(role.Name); result2.Err != nil {
   555  				mlog.Critical("Failed to migrate role to database.")
   556  				mlog.Critical(fmt.Sprint(result.Err))
   557  				allSucceeded = false
   558  			} else {
   559  				// If the role already existed, check it is the same and update if not.
   560  				fetchedRole := result.Data.(*model.Role)
   561  				if !reflect.DeepEqual(fetchedRole.Permissions, role.Permissions) ||
   562  					fetchedRole.DisplayName != role.DisplayName ||
   563  					fetchedRole.Description != role.Description ||
   564  					fetchedRole.SchemeManaged != role.SchemeManaged {
   565  					role.Id = fetchedRole.Id
   566  					if result := <-a.Srv.Store.Role().Save(role); result.Err != nil {
   567  						// Role is not the same, but failed to update.
   568  						mlog.Critical("Failed to migrate role to database.")
   569  						mlog.Critical(fmt.Sprint(result.Err))
   570  						allSucceeded = false
   571  					}
   572  				}
   573  			}
   574  		}
   575  	}
   576  
   577  	if !allSucceeded {
   578  		return
   579  	}
   580  
   581  	config := a.Config()
   582  	if *config.ServiceSettings.AllowEditPost == model.ALLOW_EDIT_POST_ALWAYS {
   583  		*config.ServiceSettings.PostEditTimeLimit = -1
   584  		if err := a.SaveConfig(config, true); err != nil {
   585  			mlog.Error("Failed to update config in Advanced Permissions Phase 1 Migration.", mlog.String("error", err.Error()))
   586  		}
   587  	}
   588  
   589  	system := model.System{
   590  		Name:  ADVANCED_PERMISSIONS_MIGRATION_KEY,
   591  		Value: "true",
   592  	}
   593  
   594  	if result := <-a.Srv.Store.System().Save(&system); result.Err != nil {
   595  		mlog.Critical("Failed to mark advanced permissions migration as completed.")
   596  		mlog.Critical(fmt.Sprint(result.Err))
   597  	}
   598  }
   599  
   600  func (a *App) SetPhase2PermissionsMigrationStatus(isComplete bool) error {
   601  	if !isComplete {
   602  		res := <-a.Srv.Store.System().PermanentDeleteByName(model.MIGRATION_KEY_ADVANCED_PERMISSIONS_PHASE_2)
   603  		if res.Err != nil {
   604  			return res.Err
   605  		}
   606  	}
   607  	a.phase2PermissionsMigrationComplete = isComplete
   608  	return nil
   609  }
   610  
   611  func (a *App) DoEmojisPermissionsMigration() {
   612  	// If the migration is already marked as completed, don't do it again.
   613  	if result := <-a.Srv.Store.System().GetByName(EMOJIS_PERMISSIONS_MIGRATION_KEY); result.Err == nil {
   614  		return
   615  	}
   616  
   617  	var role *model.Role = nil
   618  	var systemAdminRole *model.Role = nil
   619  	var err *model.AppError = nil
   620  
   621  	mlog.Info("Migrating emojis config to database.")
   622  	switch *a.Config().ServiceSettings.RestrictCustomEmojiCreation {
   623  	case model.RESTRICT_EMOJI_CREATION_ALL:
   624  		role, err = a.GetRoleByName(model.SYSTEM_USER_ROLE_ID)
   625  		if err != nil {
   626  			mlog.Critical("Failed to migrate emojis creation permissions from mattermost config.")
   627  			mlog.Critical(err.Error())
   628  			return
   629  		}
   630  	case model.RESTRICT_EMOJI_CREATION_ADMIN:
   631  		role, err = a.GetRoleByName(model.TEAM_ADMIN_ROLE_ID)
   632  		if err != nil {
   633  			mlog.Critical("Failed to migrate emojis creation permissions from mattermost config.")
   634  			mlog.Critical(err.Error())
   635  			return
   636  		}
   637  	case model.RESTRICT_EMOJI_CREATION_SYSTEM_ADMIN:
   638  		role = nil
   639  	default:
   640  		mlog.Critical("Failed to migrate emojis creation permissions from mattermost config.")
   641  		mlog.Critical("Invalid restrict emoji creation setting")
   642  		return
   643  	}
   644  
   645  	if role != nil {
   646  		role.Permissions = append(role.Permissions, model.PERMISSION_MANAGE_EMOJIS.Id)
   647  		if result := <-a.Srv.Store.Role().Save(role); result.Err != nil {
   648  			mlog.Critical("Failed to migrate emojis creation permissions from mattermost config.")
   649  			mlog.Critical(result.Err.Error())
   650  			return
   651  		}
   652  	}
   653  
   654  	systemAdminRole, err = a.GetRoleByName(model.SYSTEM_ADMIN_ROLE_ID)
   655  	if err != nil {
   656  		mlog.Critical("Failed to migrate emojis creation permissions from mattermost config.")
   657  		mlog.Critical(err.Error())
   658  		return
   659  	}
   660  
   661  	systemAdminRole.Permissions = append(systemAdminRole.Permissions, model.PERMISSION_MANAGE_EMOJIS.Id)
   662  	systemAdminRole.Permissions = append(systemAdminRole.Permissions, model.PERMISSION_MANAGE_OTHERS_EMOJIS.Id)
   663  	if result := <-a.Srv.Store.Role().Save(systemAdminRole); result.Err != nil {
   664  		mlog.Critical("Failed to migrate emojis creation permissions from mattermost config.")
   665  		mlog.Critical(result.Err.Error())
   666  		return
   667  	}
   668  
   669  	system := model.System{
   670  		Name:  EMOJIS_PERMISSIONS_MIGRATION_KEY,
   671  		Value: "true",
   672  	}
   673  
   674  	if result := <-a.Srv.Store.System().Save(&system); result.Err != nil {
   675  		mlog.Critical("Failed to mark emojis permissions migration as completed.")
   676  		mlog.Critical(fmt.Sprint(result.Err))
   677  	}
   678  }
   679  
   680  func (a *App) StartElasticsearch() {
   681  	a.Go(func() {
   682  		if err := a.Elasticsearch.Start(); err != nil {
   683  			mlog.Error(err.Error())
   684  		}
   685  	})
   686  
   687  	a.AddConfigListener(func(oldConfig *model.Config, newConfig *model.Config) {
   688  		if !*oldConfig.ElasticsearchSettings.EnableIndexing && *newConfig.ElasticsearchSettings.EnableIndexing {
   689  			a.Go(func() {
   690  				if err := a.Elasticsearch.Start(); err != nil {
   691  					mlog.Error(err.Error())
   692  				}
   693  			})
   694  		} else if *oldConfig.ElasticsearchSettings.EnableIndexing && !*newConfig.ElasticsearchSettings.EnableIndexing {
   695  			a.Go(func() {
   696  				if err := a.Elasticsearch.Stop(); err != nil {
   697  					mlog.Error(err.Error())
   698  				}
   699  			})
   700  		} else if *oldConfig.ElasticsearchSettings.Password != *newConfig.ElasticsearchSettings.Password || *oldConfig.ElasticsearchSettings.Username != *newConfig.ElasticsearchSettings.Username || *oldConfig.ElasticsearchSettings.ConnectionUrl != *newConfig.ElasticsearchSettings.ConnectionUrl || *oldConfig.ElasticsearchSettings.Sniff != *newConfig.ElasticsearchSettings.Sniff {
   701  			a.Go(func() {
   702  				if *oldConfig.ElasticsearchSettings.EnableIndexing {
   703  					if err := a.Elasticsearch.Stop(); err != nil {
   704  						mlog.Error(err.Error())
   705  					}
   706  					if err := a.Elasticsearch.Start(); err != nil {
   707  						mlog.Error(err.Error())
   708  					}
   709  				}
   710  			})
   711  		}
   712  	})
   713  
   714  	a.AddLicenseListener(func() {
   715  		if a.License() != nil {
   716  			a.Go(func() {
   717  				if err := a.Elasticsearch.Start(); err != nil {
   718  					mlog.Error(err.Error())
   719  				}
   720  			})
   721  		} else {
   722  			a.Go(func() {
   723  				if err := a.Elasticsearch.Stop(); err != nil {
   724  					mlog.Error(err.Error())
   725  				}
   726  			})
   727  		}
   728  	})
   729  }
   730  
   731  func (a *App) getSystemInstallDate() (int64, *model.AppError) {
   732  	result := <-a.Srv.Store.System().GetByName(model.SYSTEM_INSTALLATION_DATE_KEY)
   733  	if result.Err != nil {
   734  		return 0, result.Err
   735  	}
   736  	systemData := result.Data.(*model.System)
   737  	value, err := strconv.ParseInt(systemData.Value, 10, 64)
   738  	if err != nil {
   739  		return 0, model.NewAppError("getSystemInstallDate", "app.system_install_date.parse_int.app_error", nil, err.Error(), http.StatusInternalServerError)
   740  	}
   741  	return value, nil
   742  }