app.go 21.6 KB
Newer Older
1
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
2 3 4 5 6
// See License.txt for license information.

package app

import (
7
	"crypto/ecdsa"
8
	"fmt"
Chris's avatar
Chris committed
9
	"html/template"
10
	"net/http"
11
	"path"
12
	"reflect"
13
	"strconv"
14
	"sync"
Chris's avatar
Chris committed
15 16
	"sync/atomic"

17
	"github.com/gorilla/mux"
18
	"github.com/pkg/errors"
19
	"github.com/throttled/throttled"
20

Chris's avatar
Chris committed
21
	"github.com/mattermost/mattermost-server/einterfaces"
22 23
	ejobs "github.com/mattermost/mattermost-server/einterfaces/jobs"
	"github.com/mattermost/mattermost-server/jobs"
24
	tjobs "github.com/mattermost/mattermost-server/jobs/interfaces"
25
	"github.com/mattermost/mattermost-server/mlog"
Chris's avatar
Chris committed
26
	"github.com/mattermost/mattermost-server/model"
27
	"github.com/mattermost/mattermost-server/plugin"
28
	"github.com/mattermost/mattermost-server/services/httpservice"
29 30
	"github.com/mattermost/mattermost-server/store"
	"github.com/mattermost/mattermost-server/store/sqlstore"
Chris's avatar
Chris committed
31
	"github.com/mattermost/mattermost-server/utils"
32 33
)

34
const ADVANCED_PERMISSIONS_MIGRATION_KEY = "AdvancedPermissionsMigrationComplete"
35
const EMOJIS_PERMISSIONS_MIGRATION_KEY = "EmojisPermissionsMigrationComplete"
36

Chris's avatar
Chris committed
37
type App struct {
Chris's avatar
Chris committed
38 39 40
	goroutineCount      int32
	goroutineExitSignal chan struct{}

Chris's avatar
Chris committed
41 42
	Srv *Server

43 44
	Log *mlog.Logger

45 46
	Plugins                *plugin.Environment
	PluginConfigListenerId string
Chris's avatar
Chris committed
47

48 49
	EmailBatching    *EmailBatchingJob
	EmailRateLimiter *throttled.GCRARateLimiter
50 51 52 53

	Hubs                        []*Hub
	HubsStopCheckingForDeadlock chan bool

54 55
	PushNotificationsHub PushNotificationsHub

56 57
	Jobs *jobs.JobServer

Chris's avatar
Chris committed
58 59 60
	AccountMigration einterfaces.AccountMigrationInterface
	Cluster          einterfaces.ClusterInterface
	Compliance       einterfaces.ComplianceInterface
61
	DataRetention    einterfaces.DataRetentionInterface
Chris's avatar
Chris committed
62 63
	Elasticsearch    einterfaces.ElasticsearchInterface
	Ldap             einterfaces.LdapInterface
64
	MessageExport    einterfaces.MessageExportInterface
Chris's avatar
Chris committed
65 66 67
	Metrics          einterfaces.MetricsInterface
	Mfa              einterfaces.MfaInterface
	Saml             einterfaces.SamlInterface
68

69 70 71 72 73
	config                 atomic.Value
	envConfig              map[string]interface{}
	configFile             string
	configListeners        map[string]func(*model.Config, *model.Config)
	clusterLeaderListeners sync.Map
Chris's avatar
Chris committed
74

75 76 77 78
	licenseValue       atomic.Value
	clientLicenseValue atomic.Value
	licenseListeners   map[string]func()

Chris Duarte's avatar
Chris Duarte committed
79 80
	timezones atomic.Value

Chris's avatar
Chris committed
81 82
	siteURL string

Chris's avatar
Chris committed
83
	newStore func() store.Store
84

85 86 87 88 89 90 91 92 93
	htmlTemplateWatcher     *utils.HTMLTemplateWatcher
	sessionCache            *utils.Cache
	configListenerId        string
	licenseListenerId       string
	logListenerId           string
	clusterLeaderListenerId string
	disableConfigWatch      bool
	configWatcher           *utils.ConfigWatcher
	asymmetricSigningKey    *ecdsa.PrivateKey
94 95 96

	pluginCommands     []*PluginCommand
	pluginCommandsLock sync.RWMutex
97

98 99 100 101
	clientConfig        map[string]string
	clientConfigHash    string
	limitedClientConfig map[string]string
	diagnosticId        string
102 103

	phase2PermissionsMigrationComplete bool
104

105
	HTTPService httpservice.HTTPService
Chris's avatar
Chris committed
106 107
}

108 109 110
var appCount = 0

// 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 115
	if appCount > 1 {
		panic("Only one App should exist at a time. Did you forget to call Shutdown()?")
116 117
	}

118 119
	rootRouter := mux.NewRouter()

120 121
	app := &App{
		goroutineExitSignal: make(chan struct{}, 1),
122
		Srv: &Server{
123
			RootRouter: rootRouter,
124
		},
125 126 127 128 129
		sessionCache:     utils.NewLru(model.SESSION_CACHE_SIZE),
		configFile:       "config.json",
		configListeners:  make(map[string]func(*model.Config, *model.Config)),
		clientConfig:     make(map[string]string),
		licenseListeners: map[string]func(){},
130
	}
131

132
	app.HTTPService = httpservice.MakeHTTPService(app)
133

134 135 136
	app.CreatePushNotificationsHub()
	app.StartPushNotificationsHubWorkers()

137 138 139 140 141
	defer func() {
		if outErr != nil {
			app.Shutdown()
		}
	}()
142 143 144 145 146

	for _, option := range options {
		option(app)
	}

Chris's avatar
Chris committed
147
	if utils.T == nil {
148 149 150
		if err := utils.TranslationsPreInit(); err != nil {
			return nil, errors.Wrapf(err, "unable to load Mattermost translation files")
		}
Chris's avatar
Chris committed
151
	}
152
	model.AppErrorInit(utils.T)
153

Chris's avatar
Chris committed
154 155 156
	if err := app.LoadConfig(app.configFile); err != nil {
		return nil, err
	}
157 158 159 160 161 162 163 164 165 166 167 168 169 170

	// Initalize logging
	app.Log = mlog.NewLogger(utils.MloggerConfigFromLoggerConfig(&app.Config().LogSettings))

	// Redirect default golang logger to this logger
	mlog.RedirectStdLog(app.Log)

	// Use this app logger as the global logger (eventually remove all instances of global logging)
	mlog.InitGlobalLogger(app.Log)

	app.logListenerId = app.AddConfigListener(func(_, after *model.Config) {
		app.Log.ChangeLevels(utils.MloggerConfigFromLoggerConfig(&after.LogSettings))
	})

171
	app.EnableConfigWatch()
Chris Duarte's avatar
Chris Duarte committed
172 173 174

	app.LoadTimezones()

Chris's avatar
Chris committed
175
	if err := utils.InitTranslations(app.Config().LocalizationSettings); err != nil {
176 177
		return nil, errors.Wrapf(err, "unable to load Mattermost translation files")
	}
Chris's avatar
Chris committed
178

Chris's avatar
Chris committed
179
	app.configListenerId = app.AddConfigListener(func(_, _ *model.Config) {
180
		app.configOrLicenseListener()
181 182 183

		message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CONFIG_CHANGED, "", "", "", nil)

184
		message.Add("config", app.ClientConfigWithComputed())
185 186 187 188 189 190 191 192 193 194 195 196 197
		app.Go(func() {
			app.Publish(message)
		})
	})
	app.licenseListenerId = app.AddLicenseListener(func() {
		app.configOrLicenseListener()

		message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_LICENSE_CHANGED, "", "", "", nil)
		message.Add("license", app.GetSanitizedClientLicense())
		app.Go(func() {
			app.Publish(message)
		})

Chris's avatar
Chris committed
198 199
	})

200 201 202 203
	if err := app.SetupInviteEmailRateLimiting(); err != nil {
		return nil, err
	}

204
	mlog.Info("Server is initializing...")
Chris's avatar
Chris committed
205 206 207

	app.initEnterprise()

208 209
	if app.newStore == nil {
		app.newStore = func() store.Store {
Chris's avatar
Chris committed
210
			return store.NewLayeredStore(sqlstore.NewSqlSupplier(app.Config().SqlSettings, app.Metrics), app.Metrics, app.Cluster)
211 212 213
		}
	}

Chris's avatar
Chris committed
214
	if htmlTemplateWatcher, err := utils.NewHTMLTemplateWatcher("templates"); err != nil {
215
		mlog.Error(fmt.Sprintf("Failed to parse server templates %v", err))
Chris's avatar
Chris committed
216 217 218 219
	} else {
		app.htmlTemplateWatcher = htmlTemplateWatcher
	}

220
	app.Srv.Store = app.newStore()
221

222 223 224 225 226 227 228 229
	app.AddConfigListener(func(_, current *model.Config) {
		if current.SqlSettings.EnablePublicChannelsMaterialization != nil && !*current.SqlSettings.EnablePublicChannelsMaterialization {
			app.Srv.Store.Channel().DisableExperimentalPublicChannelsMaterialization()
		} else {
			app.Srv.Store.Channel().EnableExperimentalPublicChannelsMaterialization()
		}
	})

230 231 232 233
	if err := app.ensureAsymmetricSigningKey(); err != nil {
		return nil, errors.Wrapf(err, "unable to ensure asymmetric signing key")
	}

234 235 236 237
	if err := app.ensureInstallationDate(); err != nil {
		return nil, errors.Wrapf(err, "unable to ensure installation date")
	}

238 239 240
	app.EnsureDiagnosticId()
	app.regenerateClientConfig()

241
	app.initJobs()
242 243 244
	app.AddLicenseListener(func() {
		app.initJobs()
	})
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 249 250
		app.Jobs.Schedulers.HandleClusterLeaderChange(app.IsLeader())
	})

251 252 253 254 255
	subpath, err := utils.GetSubpathFromConfig(app.Config())
	if err != nil {
		return nil, errors.Wrap(err, "failed to parse SiteURL subpath")
	}
	app.Srv.Router = app.Srv.RootRouter.PathPrefix(subpath).Subrouter()
256 257 258
	app.Srv.Router.HandleFunc("/plugins/{plugin_id:[A-Za-z0-9\\_\\-\\.]+}", app.ServePluginRequest)
	app.Srv.Router.HandleFunc("/plugins/{plugin_id:[A-Za-z0-9\\_\\-\\.]+}/{anything:.*}", app.ServePluginRequest)

259 260 261 262 263 264 265
	// If configured with a subpath, redirect 404s at the root back into the subpath.
	if subpath != "/" {
		app.Srv.RootRouter.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			r.URL.Path = path.Join(subpath, r.URL.Path)
			http.Redirect(w, r, r.URL.String(), http.StatusFound)
		})
	}
266 267 268 269 270 271 272
	app.Srv.Router.NotFoundHandler = http.HandlerFunc(app.Handle404)

	app.Srv.WebSocketRouter = &WebSocketRouter{
		app:      app,
		handlers: make(map[string]webSocketHandler),
	}

273
	return app, nil
Chris's avatar
Chris committed
274 275
}

276 277 278 279
func (a *App) configOrLicenseListener() {
	a.regenerateClientConfig()
}

280 281
func (a *App) Shutdown() {
	appCount--
Chris's avatar
Chris committed
282

283
	mlog.Info("Stopping Server...")
284

285 286
	a.StopServer()
	a.HubStop()
287
	a.StopPushNotificationsHubWorkers()
Chris's avatar
Chris committed
288

289 290
	a.ShutDownPlugins()
	a.WaitForGoroutines()
Chris's avatar
Chris committed
291

292 293 294
	if a.Srv.Store != nil {
		a.Srv.Store.Close()
	}
295
	a.Srv = nil
Chris's avatar
Chris committed
296

Chris's avatar
Chris committed
297 298 299 300
	if a.htmlTemplateWatcher != nil {
		a.htmlTemplateWatcher.Close()
	}

Chris's avatar
Chris committed
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
}

Chris's avatar
Chris committed
312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329
var accountMigrationInterface func(*App) einterfaces.AccountMigrationInterface

func RegisterAccountMigrationInterface(f func(*App) einterfaces.AccountMigrationInterface) {
	accountMigrationInterface = f
}

var clusterInterface func(*App) einterfaces.ClusterInterface

func RegisterClusterInterface(f func(*App) einterfaces.ClusterInterface) {
	clusterInterface = f
}

var complianceInterface func(*App) einterfaces.ComplianceInterface

func RegisterComplianceInterface(f func(*App) einterfaces.ComplianceInterface) {
	complianceInterface = f
}

330
var dataRetentionInterface func(*App) einterfaces.DataRetentionInterface
331

332 333 334 335
func RegisterDataRetentionInterface(f func(*App) einterfaces.DataRetentionInterface) {
	dataRetentionInterface = f
}

336 337 338 339 340 341
var elasticsearchInterface func(*App) einterfaces.ElasticsearchInterface

func RegisterElasticsearchInterface(f func(*App) einterfaces.ElasticsearchInterface) {
	elasticsearchInterface = f
}

342 343 344 345
var jobsDataRetentionJobInterface func(*App) ejobs.DataRetentionJobInterface

func RegisterJobsDataRetentionJobInterface(f func(*App) ejobs.DataRetentionJobInterface) {
	jobsDataRetentionJobInterface = f
346 347
}

348 349 350 351 352 353
var jobsMessageExportJobInterface func(*App) ejobs.MessageExportJobInterface

func RegisterJobsMessageExportJobInterface(f func(*App) ejobs.MessageExportJobInterface) {
	jobsMessageExportJobInterface = f
}

354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
var jobsElasticsearchAggregatorInterface func(*App) ejobs.ElasticsearchAggregatorInterface

func RegisterJobsElasticsearchAggregatorInterface(f func(*App) ejobs.ElasticsearchAggregatorInterface) {
	jobsElasticsearchAggregatorInterface = f
}

var jobsElasticsearchIndexerInterface func(*App) ejobs.ElasticsearchIndexerInterface

func RegisterJobsElasticsearchIndexerInterface(f func(*App) ejobs.ElasticsearchIndexerInterface) {
	jobsElasticsearchIndexerInterface = f
}

var jobsLdapSyncInterface func(*App) ejobs.LdapSyncInterface

func RegisterJobsLdapSyncInterface(f func(*App) ejobs.LdapSyncInterface) {
	jobsLdapSyncInterface = f
}

372 373 374 375 376 377
var jobsMigrationsInterface func(*App) tjobs.MigrationsJobInterface

func RegisterJobsMigrationsJobInterface(f func(*App) tjobs.MigrationsJobInterface) {
	jobsMigrationsInterface = f
}

Chris's avatar
Chris committed
378 379 380 381 382 383
var ldapInterface func(*App) einterfaces.LdapInterface

func RegisterLdapInterface(f func(*App) einterfaces.LdapInterface) {
	ldapInterface = f
}

384 385 386 387 388 389
var messageExportInterface func(*App) einterfaces.MessageExportInterface

func RegisterMessageExportInterface(f func(*App) einterfaces.MessageExportInterface) {
	messageExportInterface = f
}

Chris's avatar
Chris committed
390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417
var metricsInterface func(*App) einterfaces.MetricsInterface

func RegisterMetricsInterface(f func(*App) einterfaces.MetricsInterface) {
	metricsInterface = f
}

var mfaInterface func(*App) einterfaces.MfaInterface

func RegisterMfaInterface(f func(*App) einterfaces.MfaInterface) {
	mfaInterface = f
}

var samlInterface func(*App) einterfaces.SamlInterface

func RegisterSamlInterface(f func(*App) einterfaces.SamlInterface) {
	samlInterface = f
}

func (a *App) initEnterprise() {
	if accountMigrationInterface != nil {
		a.AccountMigration = accountMigrationInterface(a)
	}
	if clusterInterface != nil {
		a.Cluster = clusterInterface(a)
	}
	if complianceInterface != nil {
		a.Compliance = complianceInterface(a)
	}
418 419 420
	if elasticsearchInterface != nil {
		a.Elasticsearch = elasticsearchInterface(a)
	}
Chris's avatar
Chris committed
421 422
	if ldapInterface != nil {
		a.Ldap = ldapInterface(a)
Chris's avatar
Chris committed
423
		a.AddConfigListener(func(_, cfg *model.Config) {
Chris's avatar
Chris committed
424 425 426 427 428
			if err := utils.ValidateLdapFilter(cfg, a.Ldap); err != nil {
				panic(utils.T(err.Id))
			}
		})
	}
429 430 431
	if messageExportInterface != nil {
		a.MessageExport = messageExportInterface(a)
	}
Chris's avatar
Chris committed
432 433 434 435 436 437 438 439
	if metricsInterface != nil {
		a.Metrics = metricsInterface(a)
	}
	if mfaInterface != nil {
		a.Mfa = mfaInterface(a)
	}
	if samlInterface != nil {
		a.Saml = samlInterface(a)
Chris's avatar
Chris committed
440
		a.AddConfigListener(func(_, cfg *model.Config) {
Chris's avatar
Chris committed
441 442 443
			a.Saml.ConfigureSP()
		})
	}
444 445 446
	if dataRetentionInterface != nil {
		a.DataRetention = dataRetentionInterface(a)
	}
447 448 449
}

func (a *App) initJobs() {
Chris's avatar
Chris committed
450
	a.Jobs = jobs.NewJobServer(a, a.Srv.Store)
451 452
	if jobsDataRetentionJobInterface != nil {
		a.Jobs.DataRetentionJob = jobsDataRetentionJobInterface(a)
453
	}
454 455 456
	if jobsMessageExportJobInterface != nil {
		a.Jobs.MessageExportJob = jobsMessageExportJobInterface(a)
	}
457 458 459 460 461 462 463 464 465
	if jobsElasticsearchAggregatorInterface != nil {
		a.Jobs.ElasticsearchAggregator = jobsElasticsearchAggregatorInterface(a)
	}
	if jobsElasticsearchIndexerInterface != nil {
		a.Jobs.ElasticsearchIndexer = jobsElasticsearchIndexerInterface(a)
	}
	if jobsLdapSyncInterface != nil {
		a.Jobs.LdapSync = jobsLdapSyncInterface(a)
	}
466 467 468
	if jobsMigrationsInterface != nil {
		a.Jobs.Migrations = jobsMigrationsInterface(a)
	}
469 470
	a.Jobs.Workers = a.Jobs.InitWorkers()
	a.Jobs.Schedulers = a.Jobs.InitSchedulers()
Chris's avatar
Chris committed
471 472
}

473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498
func (a *App) DiagnosticId() string {
	return a.diagnosticId
}

func (a *App) SetDiagnosticId(id string) {
	a.diagnosticId = id
}

func (a *App) EnsureDiagnosticId() {
	if a.diagnosticId != "" {
		return
	}
	if result := <-a.Srv.Store.System().Get(); result.Err == nil {
		props := result.Data.(model.StringMap)

		id := props[model.SYSTEM_DIAGNOSTIC_ID]
		if len(id) == 0 {
			id = model.NewId()
			systemId := &model.System{Name: model.SYSTEM_DIAGNOSTIC_ID, Value: id}
			<-a.Srv.Store.System().Save(systemId)
		}

		a.diagnosticId = id
	}
}

Chris's avatar
Chris committed
499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521
// Go creates a goroutine, but maintains a record of it to ensure that execution completes before
// the app is destroyed.
func (a *App) Go(f func()) {
	atomic.AddInt32(&a.goroutineCount, 1)

	go func() {
		f()

		atomic.AddInt32(&a.goroutineCount, -1)
		select {
		case a.goroutineExitSignal <- struct{}{}:
		default:
		}
	}()
}

// WaitForGoroutines blocks until all goroutines created by App.Go exit.
func (a *App) WaitForGoroutines() {
	for atomic.LoadInt32(&a.goroutineCount) != 0 {
		<-a.goroutineExitSignal
	}
}

Chris's avatar
Chris committed
522
func (a *App) HTMLTemplates() *template.Template {
523 524 525 526 527
	if a.htmlTemplateWatcher != nil {
		return a.htmlTemplateWatcher.Templates()
	}

	return nil
Chris's avatar
Chris committed
528 529
}

530 531 532
func (a *App) Handle404(w http.ResponseWriter, r *http.Request) {
	err := model.NewAppError("Handle404", "api.context.404.app_error", nil, "", http.StatusNotFound)

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 539 540 541 542 543 544

// This function migrates the default built in roles from code/config to the database.
func (a *App) DoAdvancedPermissionsMigration() {
	// If the migration is already marked as completed, don't do it again.
	if result := <-a.Srv.Store.System().GetByName(ADVANCED_PERMISSIONS_MIGRATION_KEY); result.Err == nil {
		return
	}

545
	mlog.Info("Migrating roles to database.")
546
	roles := model.MakeDefaultRoles()
547
	roles = utils.SetRolePermissionsFromConfig(roles, a.Config(), a.License() != nil)
548 549 550 551 552 553 554

	allSucceeded := true

	for _, role := range roles {
		if result := <-a.Srv.Store.Role().Save(role); result.Err != nil {
			// If this failed for reasons other than the role already existing, don't mark the migration as done.
			if result2 := <-a.Srv.Store.Role().GetByName(role.Name); result2.Err != nil {
555 556
				mlog.Critical("Failed to migrate role to database.")
				mlog.Critical(fmt.Sprint(result.Err))
557 558 559 560 561 562 563 564 565 566 567
				allSucceeded = false
			} else {
				// If the role already existed, check it is the same and update if not.
				fetchedRole := result.Data.(*model.Role)
				if !reflect.DeepEqual(fetchedRole.Permissions, role.Permissions) ||
					fetchedRole.DisplayName != role.DisplayName ||
					fetchedRole.Description != role.Description ||
					fetchedRole.SchemeManaged != role.SchemeManaged {
					role.Id = fetchedRole.Id
					if result := <-a.Srv.Store.Role().Save(role); result.Err != nil {
						// Role is not the same, but failed to update.
568 569
						mlog.Critical("Failed to migrate role to database.")
						mlog.Critical(fmt.Sprint(result.Err))
570 571 572 573 574 575 576 577 578 579 580
						allSucceeded = false
					}
				}
			}
		}
	}

	if !allSucceeded {
		return
	}

581 582 583 584 585 586 587 588
	config := a.Config()
	if *config.ServiceSettings.AllowEditPost == model.ALLOW_EDIT_POST_ALWAYS {
		*config.ServiceSettings.PostEditTimeLimit = -1
		if err := a.SaveConfig(config, true); err != nil {
			mlog.Error("Failed to update config in Advanced Permissions Phase 1 Migration.", mlog.String("error", err.Error()))
		}
	}

589 590 591 592 593 594
	system := model.System{
		Name:  ADVANCED_PERMISSIONS_MIGRATION_KEY,
		Value: "true",
	}

	if result := <-a.Srv.Store.System().Save(&system); result.Err != nil {
595 596
		mlog.Critical("Failed to mark advanced permissions migration as completed.")
		mlog.Critical(fmt.Sprint(result.Err))
597 598
	}
}
599 600 601 602 603 604 605 606 607 608 609

func (a *App) SetPhase2PermissionsMigrationStatus(isComplete bool) error {
	if !isComplete {
		res := <-a.Srv.Store.System().PermanentDeleteByName(model.MIGRATION_KEY_ADVANCED_PERMISSIONS_PHASE_2)
		if res.Err != nil {
			return res.Err
		}
	}
	a.phase2PermissionsMigrationComplete = isComplete
	return nil
}
610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678

func (a *App) DoEmojisPermissionsMigration() {
	// If the migration is already marked as completed, don't do it again.
	if result := <-a.Srv.Store.System().GetByName(EMOJIS_PERMISSIONS_MIGRATION_KEY); result.Err == nil {
		return
	}

	var role *model.Role = nil
	var systemAdminRole *model.Role = nil
	var err *model.AppError = nil

	mlog.Info("Migrating emojis config to database.")
	switch *a.Config().ServiceSettings.RestrictCustomEmojiCreation {
	case model.RESTRICT_EMOJI_CREATION_ALL:
		role, err = a.GetRoleByName(model.SYSTEM_USER_ROLE_ID)
		if err != nil {
			mlog.Critical("Failed to migrate emojis creation permissions from mattermost config.")
			mlog.Critical(err.Error())
			return
		}
	case model.RESTRICT_EMOJI_CREATION_ADMIN:
		role, err = a.GetRoleByName(model.TEAM_ADMIN_ROLE_ID)
		if err != nil {
			mlog.Critical("Failed to migrate emojis creation permissions from mattermost config.")
			mlog.Critical(err.Error())
			return
		}
	case model.RESTRICT_EMOJI_CREATION_SYSTEM_ADMIN:
		role = nil
	default:
		mlog.Critical("Failed to migrate emojis creation permissions from mattermost config.")
		mlog.Critical("Invalid restrict emoji creation setting")
		return
	}

	if role != nil {
		role.Permissions = append(role.Permissions, model.PERMISSION_MANAGE_EMOJIS.Id)
		if result := <-a.Srv.Store.Role().Save(role); result.Err != nil {
			mlog.Critical("Failed to migrate emojis creation permissions from mattermost config.")
			mlog.Critical(result.Err.Error())
			return
		}
	}

	systemAdminRole, err = a.GetRoleByName(model.SYSTEM_ADMIN_ROLE_ID)
	if err != nil {
		mlog.Critical("Failed to migrate emojis creation permissions from mattermost config.")
		mlog.Critical(err.Error())
		return
	}

	systemAdminRole.Permissions = append(systemAdminRole.Permissions, model.PERMISSION_MANAGE_EMOJIS.Id)
	systemAdminRole.Permissions = append(systemAdminRole.Permissions, model.PERMISSION_MANAGE_OTHERS_EMOJIS.Id)
	if result := <-a.Srv.Store.Role().Save(systemAdminRole); result.Err != nil {
		mlog.Critical("Failed to migrate emojis creation permissions from mattermost config.")
		mlog.Critical(result.Err.Error())
		return
	}

	system := model.System{
		Name:  EMOJIS_PERMISSIONS_MIGRATION_KEY,
		Value: "true",
	}

	if result := <-a.Srv.Store.System().Save(&system); result.Err != nil {
		mlog.Critical("Failed to mark emojis permissions migration as completed.")
		mlog.Critical(fmt.Sprint(result.Err))
	}
}
679 680 681 682 683 684 685 686 687

func (a *App) StartElasticsearch() {
	a.Go(func() {
		if err := a.Elasticsearch.Start(); err != nil {
			mlog.Error(err.Error())
		}
	})

	a.AddConfigListener(func(oldConfig *model.Config, newConfig *model.Config) {
688
		if !*oldConfig.ElasticsearchSettings.EnableIndexing && *newConfig.ElasticsearchSettings.EnableIndexing {
689 690 691 692 693
			a.Go(func() {
				if err := a.Elasticsearch.Start(); err != nil {
					mlog.Error(err.Error())
				}
			})
694
		} else if *oldConfig.ElasticsearchSettings.EnableIndexing && !*newConfig.ElasticsearchSettings.EnableIndexing {
695 696 697 698 699 700 701
			a.Go(func() {
				if err := a.Elasticsearch.Stop(); err != nil {
					mlog.Error(err.Error())
				}
			})
		} 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 {
			a.Go(func() {
702
				if *oldConfig.ElasticsearchSettings.EnableIndexing {
703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729
					if err := a.Elasticsearch.Stop(); err != nil {
						mlog.Error(err.Error())
					}
					if err := a.Elasticsearch.Start(); err != nil {
						mlog.Error(err.Error())
					}
				}
			})
		}
	})

	a.AddLicenseListener(func() {
		if a.License() != nil {
			a.Go(func() {
				if err := a.Elasticsearch.Start(); err != nil {
					mlog.Error(err.Error())
				}
			})
		} else {
			a.Go(func() {
				if err := a.Elasticsearch.Stop(); err != nil {
					mlog.Error(err.Error())
				}
			})
		}
	})
}
730 731 732 733 734 735 736 737 738 739 740 741 742

func (a *App) getSystemInstallDate() (int64, *model.AppError) {
	result := <-a.Srv.Store.System().GetByName(model.SYSTEM_INSTALLATION_DATE_KEY)
	if result.Err != nil {
		return 0, result.Err
	}
	systemData := result.Data.(*model.System)
	value, err := strconv.ParseInt(systemData.Value, 10, 64)
	if err != nil {
		return 0, model.NewAppError("getSystemInstallDate", "app.system_install_date.parse_int.app_error", nil, err.Error(), http.StatusInternalServerError)
	}
	return value, nil
}