Merge pull request #997 from mattermost/PLT-586

PLT-586 fixing issues with security alert
Этот коммит содержится в:
Corey Hulen
2015-10-12 13:56:21 -07:00
родитель 7a469c8fc1 9f83a72f73
Коммит a6a3c351a8
14 изменённых файлов: 136 добавлений и 77 удалений

Просмотреть файл

@@ -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

Просмотреть файл

@@ -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,

Просмотреть файл

@@ -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,

Просмотреть файл

@@ -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,

Просмотреть файл

@@ -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,

Просмотреть файл

@@ -81,28 +81,28 @@ func main() {
func securityAndDiagnosticsJob() {
go func() {
for {
if utils.Cfg.PrivacySettings.EnableSecurityFixAlert && model.IsOfficalBuild() {
if *utils.Cfg.ServiceSettings.EnableSecurityFixAlert {
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)
}
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)
if (currentTime - lastSecurityTime) > 1000*60*60*24*1 {
l4g.Info("Checking for security update from Mattermost")
l4g.Debug("Checking for security update from Mattermost")
id := props["DiagnosticId"]
if len(id) == 0 {
id = model.NewId()
systemId := &model.System{Name: "DiagnosticId", Value: id}
<-api.Srv.Store.System().Save(systemId)
}
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.")

Просмотреть файл

@@ -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 {

Просмотреть файл

@@ -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
}

Просмотреть файл

@@ -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()

Просмотреть файл

@@ -104,6 +104,7 @@ type UserStore interface {
UpdateFailedPasswordAttempts(userId string, attempts int) StoreChannel
GetForExport(teamId string) StoreChannel
GetTotalUsersCount() StoreChannel
GetTotalActiveUsersCount() StoreChannel
GetSystemAdminProfiles() StoreChannel
}

Просмотреть файл

@@ -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)
}

Просмотреть файл

@@ -6,24 +6,23 @@ package utils
import (
"net/http"
"net/url"
"github.com/mattermost/platform/model"
)
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 {
res, err := http.Get(DIAGNOSTIC_URL + "/i?" + values.Encode())
if err != nil {

Просмотреть файл

@@ -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,
@@ -137,39 +136,6 @@ export default class PrivacySettings extends React.Component {
</div>
</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}

Просмотреть файл

@@ -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))) {
@@ -304,6 +306,39 @@ export default class ServiceSettings extends React.Component {
</div>
</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}