diff --git a/app/channels.go b/app/channels.go index 7378f3f625..0008e9f463 100644 --- a/app/channels.go +++ b/app/channels.go @@ -4,8 +4,11 @@ package app import ( + "sync/atomic" + "github.com/mattermost/mattermost-server/v6/model" "github.com/mattermost/mattermost-server/v6/services/imageproxy" + "github.com/pkg/errors" ) // Channels contains all channels related state. @@ -14,6 +17,11 @@ type Channels struct { imageProxy *imageproxy.ImageProxy + asymmetricSigningKey atomic.Value + clientConfig atomic.Value + clientConfigHash atomic.Value + limitedClientConfig atomic.Value + // cached counts that are used during notice condition validation cachedPostCount int64 cachedUserCount int64 @@ -35,10 +43,13 @@ func NewChannels(s *Server) (*Channels, error) { }, nil } -func (c *Channels) Start() error { +func (ch *Channels) Start() error { + if err := ch.ensureAsymmetricSigningKey(); err != nil { + return errors.Wrapf(err, "unable to ensure asymmetric signing key") + } return nil } -func (c *Channels) Stop() error { +func (*Channels) Stop() error { return nil } diff --git a/app/config.go b/app/config.go index c1161f8d0d..df080b04c4 100644 --- a/app/config.go +++ b/app/config.go @@ -75,15 +75,15 @@ func (a *App) ReloadConfig() error { } func (a *App) ClientConfig() map[string]string { - return a.Srv().clientConfig.Load().(map[string]string) + return a.ch.clientConfig.Load().(map[string]string) } func (a *App) ClientConfigHash() string { - return a.Srv().ClientConfigHash() + return a.ch.ClientConfigHash() } func (a *App) LimitedClientConfig() map[string]string { - return a.Srv().limitedClientConfig.Load().(map[string]string) + return a.ch.limitedClientConfig.Load().(map[string]string) } // Registers a function with a given listener to be called when the config is reloaded and may have changed. The function @@ -168,14 +168,14 @@ func (s *Server) ensurePostActionCookieSecret() error { // ensureAsymmetricSigningKey ensures that an asymmetric signing key exists and future calls to // AsymmetricSigningKey will always return a valid signing key. -func (s *Server) ensureAsymmetricSigningKey() error { - if s.AsymmetricSigningKey() != nil { +func (ch *Channels) ensureAsymmetricSigningKey() error { + if ch.AsymmetricSigningKey() != nil { return nil } var key *model.SystemAsymmetricSigningKey - value, err := s.Store.System().GetByName(model.SystemAsymmetricSigningKeyKey) + value, err := ch.srv.Store.System().GetByName(model.SystemAsymmetricSigningKeyKey) if err == nil { if err := json.Unmarshal([]byte(value.Value), &key); err != nil { return err @@ -205,7 +205,7 @@ func (s *Server) ensureAsymmetricSigningKey() error { } system.Value = string(v) // If we were able to save the key, use it, otherwise log the error. - if err = s.Store.System().Save(system); err != nil { + if err = ch.srv.Store.System().Save(system); err != nil { mlog.Warn("Failed to save AsymmetricSigningKey", mlog.Err(err)) } else { key = newKey @@ -215,7 +215,7 @@ func (s *Server) ensureAsymmetricSigningKey() error { // If we weren't able to save a new key above, another server must have beat us to it. Get the // key from the database, and if that fails, error out. if key == nil { - value, err := s.Store.System().GetByName(model.SystemAsymmetricSigningKeyKey) + value, err := ch.srv.Store.System().GetByName(model.SystemAsymmetricSigningKeyKey) if err != nil { return err } @@ -232,7 +232,7 @@ func (s *Server) ensureAsymmetricSigningKey() error { default: return fmt.Errorf("unknown curve: " + key.ECDSAKey.Curve) } - s.asymmetricSigningKey.Store(&ecdsa.PrivateKey{ + ch.asymmetricSigningKey.Store(&ecdsa.PrivateKey{ PublicKey: ecdsa.PublicKey{ Curve: curve, X: key.ECDSAKey.X, @@ -240,7 +240,7 @@ func (s *Server) ensureAsymmetricSigningKey() error { }, D: key.ECDSAKey.D, }) - s.regenerateClientConfig() + ch.regenerateClientConfig() return nil } @@ -283,15 +283,15 @@ func (s *Server) ensureFirstServerRunTimestamp() error { } // AsymmetricSigningKey will return a private key that can be used for asymmetric signing. -func (s *Server) AsymmetricSigningKey() *ecdsa.PrivateKey { - if key := s.asymmetricSigningKey.Load(); key != nil { +func (ch *Channels) AsymmetricSigningKey() *ecdsa.PrivateKey { + if key := ch.asymmetricSigningKey.Load(); key != nil { return key.(*ecdsa.PrivateKey) } return nil } func (a *App) AsymmetricSigningKey() *ecdsa.PrivateKey { - return a.Srv().AsymmetricSigningKey() + return a.ch.AsymmetricSigningKey() } func (s *Server) PostActionCookieSecret() []byte { @@ -302,12 +302,12 @@ func (a *App) PostActionCookieSecret() []byte { return a.Srv().PostActionCookieSecret() } -func (s *Server) regenerateClientConfig() { - clientConfig := config.GenerateClientConfig(s.Config(), s.TelemetryId(), s.License()) - limitedClientConfig := config.GenerateLimitedClientConfig(s.Config(), s.TelemetryId(), s.License()) +func (ch *Channels) regenerateClientConfig() { + clientConfig := config.GenerateClientConfig(ch.srv.Config(), ch.srv.TelemetryId(), ch.srv.License()) + limitedClientConfig := config.GenerateLimitedClientConfig(ch.srv.Config(), ch.srv.TelemetryId(), ch.srv.License()) if clientConfig["EnableCustomTermsOfService"] == "true" { - termsOfService, err := s.Store.TermsOfService().GetLatest(true) + termsOfService, err := ch.srv.Store.TermsOfService().GetLatest(true) if err != nil { mlog.Err(err) } else { @@ -316,16 +316,16 @@ func (s *Server) regenerateClientConfig() { } } - if key := s.AsymmetricSigningKey(); key != nil { + if key := ch.AsymmetricSigningKey(); key != nil { der, _ := x509.MarshalPKIXPublicKey(&key.PublicKey) clientConfig["AsymmetricSigningPublicKey"] = base64.StdEncoding.EncodeToString(der) limitedClientConfig["AsymmetricSigningPublicKey"] = base64.StdEncoding.EncodeToString(der) } clientConfigJSON, _ := json.Marshal(clientConfig) - s.clientConfig.Store(clientConfig) - s.limitedClientConfig.Store(limitedClientConfig) - s.clientConfigHash.Store(fmt.Sprintf("%x", md5.Sum(clientConfigJSON))) + ch.clientConfig.Store(clientConfig) + ch.limitedClientConfig.Store(limitedClientConfig) + ch.clientConfigHash.Store(fmt.Sprintf("%x", md5.Sum(clientConfigJSON))) } func (a *App) GetCookieDomain() string { @@ -342,30 +342,25 @@ func (a *App) GetSiteURL() string { } // ClientConfigWithComputed gets the configuration in a format suitable for sending to the client. -func (s *Server) ClientConfigWithComputed() map[string]string { +func (a *App) ClientConfigWithComputed() map[string]string { respCfg := map[string]string{} - for k, v := range s.clientConfig.Load().(map[string]string) { + for k, v := range a.ch.clientConfig.Load().(map[string]string) { respCfg[k] = v } // These properties are not configurable, but nevertheless represent configuration expected // by the client. - respCfg["NoAccounts"] = strconv.FormatBool(s.userService.IsFirstUserAccount()) - respCfg["MaxPostSize"] = strconv.Itoa(s.MaxPostSize()) - respCfg["UpgradedFromTE"] = strconv.FormatBool(s.isUpgradedFromTE()) + respCfg["NoAccounts"] = strconv.FormatBool(a.ch.srv.userService.IsFirstUserAccount()) + respCfg["MaxPostSize"] = strconv.Itoa(a.ch.srv.MaxPostSize()) + respCfg["UpgradedFromTE"] = strconv.FormatBool(a.ch.srv.isUpgradedFromTE()) respCfg["InstallationDate"] = "" - if installationDate, err := s.getSystemInstallDate(); err == nil { + if installationDate, err := a.ch.srv.getSystemInstallDate(); err == nil { respCfg["InstallationDate"] = strconv.FormatInt(installationDate, 10) } return respCfg } -// ClientConfigWithComputed gets the configuration in a format suitable for sending to the client. -func (a *App) ClientConfigWithComputed() map[string]string { - return a.Srv().ClientConfigWithComputed() -} - // LimitedClientConfigWithComputed gets the configuration in a format suitable for sending to the client. func (a *App) LimitedClientConfigWithComputed() map[string]string { respCfg := map[string]string{} diff --git a/app/server.go b/app/server.go index cedc3f9e6c..5761ef1155 100644 --- a/app/server.go +++ b/app/server.go @@ -150,11 +150,6 @@ type Server struct { pluginCommands []*PluginCommand pluginCommandsLock sync.RWMutex - asymmetricSigningKey atomic.Value - clientConfig atomic.Value - clientConfigHash atomic.Value - limitedClientConfig atomic.Value - telemetryService *telemetry.TelemetryService userService *users.UserService teamService *teams.TeamService @@ -447,17 +442,19 @@ func NewServer(options ...Option) (*Server, error) { } s.configListenerId = s.AddConfigListener(func(_, _ *model.Config) { - s.configOrLicenseListener() + ch := s.Channels() + ch.regenerateClientConfig() message := model.NewWebSocketEvent(model.WebsocketEventConfigChanged, "", "", "", nil) - message.Add("config", s.ClientConfigWithComputed()) + appInstance := New(ServerConnector(ch)) + message.Add("config", appInstance.ClientConfigWithComputed()) s.Go(func() { s.Publish(message) }) }) s.licenseListenerId = s.AddLicenseListener(func(oldLicense, newLicense *model.License) { - s.configOrLicenseListener() + s.Channels().regenerateClientConfig() message := model.NewWebSocketEvent(model.WebsocketEventLicenseChanged, "", "", "", nil) message.Add("license", s.GetSanitizedClientLicense()) @@ -503,10 +500,6 @@ func NewServer(options ...Option) (*Server, error) { s.Cluster.StartInterNodeCommunication() } - if err = s.ensureAsymmetricSigningKey(); err != nil { - return nil, errors.Wrapf(err, "unable to ensure asymmetric signing key") - } - if err = s.ensurePostActionCookieSecret(); err != nil { return nil, errors.Wrapf(err, "unable to ensure PostAction cookie secret") } @@ -519,8 +512,6 @@ func NewServer(options ...Option) (*Server, error) { return nil, errors.Wrapf(err, "unable to ensure first run timestamp") } - s.regenerateClientConfig() - subpath, err := utils.GetSubpathFromConfig(s.Config()) if err != nil { return nil, errors.Wrap(err, "failed to parse SiteURL subpath") @@ -1914,12 +1905,8 @@ func (s *Server) ClusterHealthScore() int { return s.Cluster.HealthScore() } -func (s *Server) configOrLicenseListener() { - s.regenerateClientConfig() -} - -func (s *Server) ClientConfigHash() string { - return s.clientConfigHash.Load().(string) +func (ch *Channels) ClientConfigHash() string { + return ch.clientConfigHash.Load().(string) } func (s *Server) initJobs() { diff --git a/services/configservice/configservice.go b/services/configservice/configservice.go index 67b9ca01a0..6bf2fb420e 100644 --- a/services/configservice/configservice.go +++ b/services/configservice/configservice.go @@ -4,8 +4,6 @@ package configservice import ( - "crypto/ecdsa" - "github.com/mattermost/mattermost-server/v6/model" ) @@ -14,5 +12,4 @@ type ConfigService interface { Config() *model.Config AddConfigListener(func(old, current *model.Config)) string RemoveConfigListener(string) - AsymmetricSigningKey() *ecdsa.PrivateKey }