diff options
author | =Corey Hulen <corey@hulen.com> | 2015-10-09 12:24:39 -0700 |
---|---|---|
committer | =Corey Hulen <corey@hulen.com> | 2015-10-09 12:24:39 -0700 |
commit | 61f92517f317f4b3f437b8db48f31c569ceb5f2d (patch) | |
tree | 23b9d7227a502a54187af96ec91eed6e1f4b7d69 | |
parent | 12cd7c03d6676466f7b10b8e80b4e05edab0cf2a (diff) | |
download | chat-61f92517f317f4b3f437b8db48f31c569ceb5f2d.tar.gz chat-61f92517f317f4b3f437b8db48f31c569ceb5f2d.tar.bz2 chat-61f92517f317f4b3f437b8db48f31c569ceb5f2d.zip |
PLT-586 fixing issues with security alert
-rw-r--r-- | api/admin.go | 2 | ||||
-rw-r--r-- | config/config.json | 6 | ||||
-rw-r--r-- | docker/1.1/config_docker.json | 6 | ||||
-rw-r--r-- | docker/dev/config_docker.json | 6 | ||||
-rw-r--r-- | docker/local/config_docker.json | 6 | ||||
-rw-r--r-- | mattermost.go | 38 | ||||
-rw-r--r-- | model/config.go | 13 | ||||
-rw-r--r-- | store/sql_user_store.go | 21 | ||||
-rw-r--r-- | store/sql_user_store_test.go | 20 | ||||
-rw-r--r-- | store/store.go | 1 | ||||
-rw-r--r-- | utils/config.go | 2 | ||||
-rw-r--r-- | utils/diagnostic.go | 17 | ||||
-rw-r--r-- | web/react/components/admin_console/privacy_settings.jsx | 34 | ||||
-rw-r--r-- | web/react/components/admin_console/service_settings.jsx | 37 |
14 files changed, 135 insertions, 74 deletions
diff --git a/api/admin.go b/api/admin.go index 2167868e0..cd1e5d2de 100644 --- a/api/admin.go +++ b/api/admin.go @@ -104,6 +104,8 @@ func saveConfig(c *Context, w http.ResponseWriter, r *http.Request) { return } + cfg.SetDefaults() + if err := cfg.IsValid(); err != nil { c.Err = err return diff --git a/config/config.json b/config/config.json index 919737da7..8ef151350 100644 --- a/config/config.json +++ b/config/config.json @@ -8,7 +8,8 @@ "EnableIncomingWebhooks": true, "EnablePostUsernameOverride": false, "EnablePostIconOverride": false, - "EnableTesting": false + "EnableTesting": false, + "EnableSecurityFixAlert": true }, "TeamSettings": { "SiteName": "Mattermost", @@ -77,8 +78,7 @@ }, "PrivacySettings": { "ShowEmailAddress": true, - "ShowFullName": true, - "EnableSecurityFixAlert": true + "ShowFullName": true }, "GitLabSettings": { "Enable": false, diff --git a/docker/1.1/config_docker.json b/docker/1.1/config_docker.json index ab5b0a7be..653b6ffd7 100644 --- a/docker/1.1/config_docker.json +++ b/docker/1.1/config_docker.json @@ -8,7 +8,8 @@ "EnableIncomingWebhooks": true, "EnablePostUsernameOverride": false, "EnablePostIconOverride": false, - "EnableTesting": false + "EnableTesting": false, + "EnableSecurityFixAlert": true }, "TeamSettings": { "SiteName": "Mattermost", @@ -77,8 +78,7 @@ }, "PrivacySettings": { "ShowEmailAddress": true, - "ShowFullName": true, - "EnableSecurityFixAlert": true + "ShowFullName": true }, "GitLabSettings": { "Enable": false, diff --git a/docker/dev/config_docker.json b/docker/dev/config_docker.json index ab5b0a7be..653b6ffd7 100644 --- a/docker/dev/config_docker.json +++ b/docker/dev/config_docker.json @@ -8,7 +8,8 @@ "EnableIncomingWebhooks": true, "EnablePostUsernameOverride": false, "EnablePostIconOverride": false, - "EnableTesting": false + "EnableTesting": false, + "EnableSecurityFixAlert": true }, "TeamSettings": { "SiteName": "Mattermost", @@ -77,8 +78,7 @@ }, "PrivacySettings": { "ShowEmailAddress": true, - "ShowFullName": true, - "EnableSecurityFixAlert": true + "ShowFullName": true }, "GitLabSettings": { "Enable": false, diff --git a/docker/local/config_docker.json b/docker/local/config_docker.json index ab5b0a7be..653b6ffd7 100644 --- a/docker/local/config_docker.json +++ b/docker/local/config_docker.json @@ -8,7 +8,8 @@ "EnableIncomingWebhooks": true, "EnablePostUsernameOverride": false, "EnablePostIconOverride": false, - "EnableTesting": false + "EnableTesting": false, + "EnableSecurityFixAlert": true }, "TeamSettings": { "SiteName": "Mattermost", @@ -77,8 +78,7 @@ }, "PrivacySettings": { "ShowEmailAddress": true, - "ShowFullName": true, - "EnableSecurityFixAlert": true + "ShowFullName": true }, "GitLabSettings": { "Enable": false, diff --git a/mattermost.go b/mattermost.go index 6c0f0a1bf..b8a44d26e 100644 --- a/mattermost.go +++ b/mattermost.go @@ -81,28 +81,28 @@ func main() { func securityAndDiagnosticsJob() { go func() { for { - if utils.Cfg.PrivacySettings.EnableSecurityFixAlert && model.IsOfficalBuild() { + if *utils.Cfg.ServiceSettings.EnableSecurityFixAlert { // && model.IsOfficalBuild() { if result := <-api.Srv.Store.System().Get(); result.Err == nil { props := result.Data.(model.StringMap) lastSecurityTime, _ := strconv.ParseInt(props["LastSecurityTime"], 10, 0) currentTime := model.GetMillis() - id := props["DiagnosticId"] - if len(id) == 0 { - id = model.NewId() - systemId := &model.System{Name: "DiagnosticId", Value: id} - <-api.Srv.Store.System().Save(systemId) - } + if (currentTime - lastSecurityTime) > 1000*60*60*24*1 { + l4g.Debug("Checking for security update from Mattermost") - v := url.Values{} - v.Set(utils.PROP_DIAGNOSTIC_ID, id) - v.Set(utils.PROP_DIAGNOSTIC_BUILD, model.CurrentVersion+"."+model.BuildNumber) - v.Set(utils.PROP_DIAGNOSTIC_DATABASE, utils.Cfg.SqlSettings.DriverName) - v.Set(utils.PROP_DIAGNOSTIC_OS, runtime.GOOS) - v.Set(utils.PROP_DIAGNOSTIC_CATEGORY, utils.VAL_DIAGNOSTIC_CATEGORY_DEFAULT) + id := props["DiagnosticId"] + if len(id) == 0 { + id = model.NewId() + systemId := &model.System{Name: "DiagnosticId", Value: id} + <-api.Srv.Store.System().Save(systemId) + } - if (currentTime - lastSecurityTime) > 1000*60*60*24*1 { - l4g.Info("Checking for security update from Mattermost") + v := url.Values{} + v.Set(utils.PROP_DIAGNOSTIC_ID, id) + v.Set(utils.PROP_DIAGNOSTIC_BUILD, model.CurrentVersion+"."+model.BuildNumber) + v.Set(utils.PROP_DIAGNOSTIC_DATABASE, utils.Cfg.SqlSettings.DriverName) + v.Set(utils.PROP_DIAGNOSTIC_OS, runtime.GOOS) + v.Set(utils.PROP_DIAGNOSTIC_CATEGORY, utils.VAL_DIAGNOSTIC_CATEGORY_DEFAULT) systemSecurityLastTime := &model.System{Name: "LastSecurityTime", Value: strconv.FormatInt(currentTime, 10)} if lastSecurityTime == 0 { @@ -111,6 +111,14 @@ func securityAndDiagnosticsJob() { <-api.Srv.Store.System().Update(systemSecurityLastTime) } + if ucr := <-api.Srv.Store.User().GetTotalUsersCount(); ucr.Err == nil { + v.Set(utils.PROP_DIAGNOSTIC_USER_COUNT, strconv.FormatInt(ucr.Data.(int64), 10)) + } + + if ucr := <-api.Srv.Store.User().GetTotalActiveUsersCount(); ucr.Err == nil { + v.Set(utils.PROP_DIAGNOSTIC_ACTIVE_USER_COUNT, strconv.FormatInt(ucr.Data.(int64), 10)) + } + res, err := http.Get(utils.DIAGNOSTIC_URL + "/security?" + v.Encode()) if err != nil { l4g.Error("Failed to get security update information from Mattermost.") diff --git a/model/config.go b/model/config.go index e4b99ad4a..8a11b7bb7 100644 --- a/model/config.go +++ b/model/config.go @@ -32,6 +32,7 @@ type ServiceSettings struct { EnablePostUsernameOverride bool EnablePostIconOverride bool EnableTesting bool + EnableSecurityFixAlert *bool } type SSOSettings struct { @@ -110,9 +111,8 @@ type RateLimitSettings struct { } type PrivacySettings struct { - ShowEmailAddress bool - ShowFullName bool - EnableSecurityFixAlert bool + ShowEmailAddress bool + ShowFullName bool } type TeamSettings struct { @@ -163,6 +163,13 @@ func ConfigFromJson(data io.Reader) *Config { } } +func (o *Config) SetDefaults() { + if o.ServiceSettings.EnableSecurityFixAlert == nil { + o.ServiceSettings.EnableSecurityFixAlert = new(bool) + *o.ServiceSettings.EnableSecurityFixAlert = true + } +} + func (o *Config) IsValid() *AppError { if o.ServiceSettings.MaximumLoginAttempts <= 0 { diff --git a/store/sql_user_store.go b/store/sql_user_store.go index 011acd7e4..dc6b07a16 100644 --- a/store/sql_user_store.go +++ b/store/sql_user_store.go @@ -530,3 +530,24 @@ func (us SqlUserStore) GetTotalUsersCount() StoreChannel { return storeChannel } + +func (us SqlUserStore) GetTotalActiveUsersCount() StoreChannel { + storeChannel := make(StoreChannel) + + go func() { + result := StoreResult{} + + time := model.GetMillis() - (1000 * 60 * 60 * 12) + + if count, err := us.GetReplica().SelectInt("SELECT COUNT(Id) FROM Users WHERE LastActivityAt > :Time", map[string]interface{}{"Time": time}); err != nil { + result.Err = model.NewAppError("SqlUserStore.GetTotalActiveUsersCount", "We could not count the users", err.Error()) + } else { + result.Data = count + } + + storeChannel <- result + close(storeChannel) + }() + + return storeChannel +} diff --git a/store/sql_user_store_test.go b/store/sql_user_store_test.go index be21c8bd2..874baf634 100644 --- a/store/sql_user_store_test.go +++ b/store/sql_user_store_test.go @@ -206,7 +206,7 @@ func TestUserStoreGet(t *testing.T) { } } -func TestUserCountt(t *testing.T) { +func TestUserCount(t *testing.T) { Setup() u1 := model.User{} @@ -224,6 +224,24 @@ func TestUserCountt(t *testing.T) { } } +func TestActiveUserCount(t *testing.T) { + Setup() + + u1 := model.User{} + u1.TeamId = model.NewId() + u1.Email = model.NewId() + Must(store.User().Save(&u1)) + + if result := <-store.User().GetTotalActiveUsersCount(); result.Err != nil { + t.Fatal(result.Err) + } else { + count := result.Data.(int64) + if count <= 0 { + t.Fatal() + } + } +} + func TestUserStoreGetProfiles(t *testing.T) { Setup() diff --git a/store/store.go b/store/store.go index 1c4d08e36..e539bc98a 100644 --- a/store/store.go +++ b/store/store.go @@ -104,6 +104,7 @@ type UserStore interface { UpdateFailedPasswordAttempts(userId string, attempts int) StoreChannel GetForExport(teamId string) StoreChannel GetTotalUsersCount() StoreChannel + GetTotalActiveUsersCount() StoreChannel GetSystemAdminProfiles() StoreChannel } diff --git a/utils/config.go b/utils/config.go index 44ee14a6e..2c6f30bf0 100644 --- a/utils/config.go +++ b/utils/config.go @@ -150,6 +150,8 @@ func LoadConfig(fileName string) { CfgFileName = fileName } + config.SetDefaults() + if err := config.IsValid(); err != nil { panic("Error validating config file=" + fileName + ", err=" + err.Message) } diff --git a/utils/diagnostic.go b/utils/diagnostic.go index da02e771b..7d13e0e52 100644 --- a/utils/diagnostic.go +++ b/utils/diagnostic.go @@ -13,17 +13,18 @@ import ( const ( DIAGNOSTIC_URL = "https://d7zmvsa9e04kk.cloudfront.net" - PROP_DIAGNOSTIC_ID = "id" - PROP_DIAGNOSTIC_CATEGORY = "c" - VAL_DIAGNOSTIC_CATEGORY_DEFAULT = "d" - PROP_DIAGNOSTIC_BUILD = "b" - PROP_DIAGNOSTIC_DATABASE = "db" - PROP_DIAGNOSTIC_OS = "os" - PROP_DIAGNOSTIC_USER_COUNT = "uc" + PROP_DIAGNOSTIC_ID = "id" + PROP_DIAGNOSTIC_CATEGORY = "c" + VAL_DIAGNOSTIC_CATEGORY_DEFAULT = "d" + PROP_DIAGNOSTIC_BUILD = "b" + PROP_DIAGNOSTIC_DATABASE = "db" + PROP_DIAGNOSTIC_OS = "os" + PROP_DIAGNOSTIC_USER_COUNT = "uc" + PROP_DIAGNOSTIC_ACTIVE_USER_COUNT = "auc" ) func SendDiagnostic(values url.Values) { - if Cfg.PrivacySettings.EnableSecurityFixAlert && model.IsOfficalBuild() { + if *Cfg.ServiceSettings.EnableSecurityFixAlert && model.IsOfficalBuild() { res, err := http.Get(DIAGNOSTIC_URL + "/i?" + values.Encode()) if err != nil { diff --git a/web/react/components/admin_console/privacy_settings.jsx b/web/react/components/admin_console/privacy_settings.jsx index a32ca3136..70ec04f4a 100644 --- a/web/react/components/admin_console/privacy_settings.jsx +++ b/web/react/components/admin_console/privacy_settings.jsx @@ -30,7 +30,6 @@ export default class PrivacySettings extends React.Component { var config = this.props.config; config.PrivacySettings.ShowEmailAddress = React.findDOMNode(this.refs.ShowEmailAddress).checked; config.PrivacySettings.ShowFullName = React.findDOMNode(this.refs.ShowFullName).checked; - config.PrivacySettings.EnableSecurityFixAlert = React.findDOMNode(this.refs.EnableSecurityFixAlert).checked; Client.saveConfig( config, @@ -138,39 +137,6 @@ export default class PrivacySettings extends React.Component { </div> <div className='form-group'> - <label - className='control-label col-sm-4' - htmlFor='EnableSecurityFixAlert' - > - {'Send Error and Diagnostic: '} - </label> - <div className='col-sm-8'> - <label className='radio-inline'> - <input - type='radio' - name='EnableSecurityFixAlert' - value='true' - ref='EnableSecurityFixAlert' - defaultChecked={this.props.config.PrivacySettings.EnableSecurityFixAlert} - onChange={this.handleChange} - /> - {'true'} - </label> - <label className='radio-inline'> - <input - type='radio' - name='EnableSecurityFixAlert' - value='false' - defaultChecked={!this.props.config.PrivacySettings.EnableSecurityFixAlert} - onChange={this.handleChange} - /> - {'false'} - </label> - <p className='help-text'>{'When true, System Administrators are notified by email if a relevant security fix alert has been announced in the last 12 hours. Requires email to be enabled.'}</p> - </div> - </div> - - <div className='form-group'> <div className='col-sm-12'> {serverError} <button diff --git a/web/react/components/admin_console/service_settings.jsx b/web/react/components/admin_console/service_settings.jsx index 3968d9820..f29d62646 100644 --- a/web/react/components/admin_console/service_settings.jsx +++ b/web/react/components/admin_console/service_settings.jsx @@ -35,11 +35,13 @@ export default class ServiceSettings extends React.Component { config.ServiceSettings.SegmentDeveloperKey = React.findDOMNode(this.refs.SegmentDeveloperKey).value.trim(); config.ServiceSettings.GoogleDeveloperKey = React.findDOMNode(this.refs.GoogleDeveloperKey).value.trim(); - //config.ServiceSettings.EnableOAuthServiceProvider = React.findDOMNode(this.refs.EnableOAuthServiceProvider).checked; config.ServiceSettings.EnableIncomingWebhooks = React.findDOMNode(this.refs.EnableIncomingWebhooks).checked; config.ServiceSettings.EnablePostUsernameOverride = React.findDOMNode(this.refs.EnablePostUsernameOverride).checked; config.ServiceSettings.EnablePostIconOverride = React.findDOMNode(this.refs.EnablePostIconOverride).checked; config.ServiceSettings.EnableTesting = React.findDOMNode(this.refs.EnableTesting).checked; + config.ServiceSettings.EnableSecurityFixAlert = React.findDOMNode(this.refs.EnableSecurityFixAlert).checked; + + //config.ServiceSettings.EnableOAuthServiceProvider = React.findDOMNode(this.refs.EnableOAuthServiceProvider).checked; var MaximumLoginAttempts = 10; if (!isNaN(parseInt(React.findDOMNode(this.refs.MaximumLoginAttempts).value, 10))) { @@ -305,6 +307,39 @@ export default class ServiceSettings extends React.Component { </div> <div className='form-group'> + <label + className='control-label col-sm-4' + htmlFor='EnableSecurityFixAlert' + > + {'Enable Security Alerts: '} + </label> + <div className='col-sm-8'> + <label className='radio-inline'> + <input + type='radio' + name='EnableSecurityFixAlert' + value='true' + ref='EnableSecurityFixAlert' + defaultChecked={this.props.config.ServiceSettings.EnableSecurityFixAlert} + onChange={this.handleChange} + /> + {'true'} + </label> + <label className='radio-inline'> + <input + type='radio' + name='EnableSecurityFixAlert' + value='false' + defaultChecked={!this.props.config.ServiceSettings.EnableSecurityFixAlert} + onChange={this.handleChange} + /> + {'false'} + </label> + <p className='help-text'>{'When true, System Administrators are notified by email if a relevant security fix alert has been announced in the last 12 hours. Requires email to be enabled.'}</p> + </div> + </div> + + <div className='form-group'> <div className='col-sm-12'> {serverError} <button |