From 73371fe33033f32c89bf7dd49170afc4afb1540c Mon Sep 17 00:00:00 2001 From: it33 Date: Thu, 8 Oct 2015 15:49:31 -0700 Subject: [PATCH 01/20] Updating Golang versions tested --- doc/developer/Setup.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/developer/Setup.md b/doc/developer/Setup.md index c63bde5129..afaef4ee46 100644 --- a/doc/developer/Setup.md +++ b/doc/developer/Setup.md @@ -11,7 +11,7 @@ Developer Machine Setup `docker-machine ip dev` 3. Add a line to your /etc/hosts that goes ` dockerhost` 4. Run `docker-machine env dev` and copy the export statements to your ~/.bash_profile -2. Download Go (version 1.4.2) from http://golang.org/dl/ +2. Download Go (version 1.4.2 or 1.5.1. Final release bits are built with 1.4.2) from http://golang.org/dl/ 3. Set up your Go workspace 1. `mkdir ~/go` 2. Add the following to your ~/.bash_profile @@ -51,7 +51,7 @@ Any issues? Please let us know on our forums at: http://forum.mattermost.org `127.0.0.1 dockerhost` 3. Install build essentials 1. `apt-get install build-essential` -4. Download Go (version 1.4.2) from http://golang.org/dl/ +4. Download Go (version 1.4.2 or 1.5.1. Final release bits are built with 1.4.2) from http://golang.org/dl/ 5. Set up your Go workspace and add Go to the PATH 1. `mkdir ~/go` 2. Add the following to your ~/.bashrc From 6aff5900f79721783599a7ab9fe29074e4050544 Mon Sep 17 00:00:00 2001 From: it33 Date: Thu, 8 Oct 2015 22:39:16 -0700 Subject: [PATCH 02/20] Fixed typos There was no space after "user name." before the next sentence. Also, "user name" should be "username" --- web/react/components/user_settings/user_settings_general.jsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/web/react/components/user_settings/user_settings_general.jsx b/web/react/components/user_settings/user_settings_general.jsx index c23c619487..66d83725cd 100644 --- a/web/react/components/user_settings/user_settings_general.jsx +++ b/web/react/components/user_settings/user_settings_general.jsx @@ -368,8 +368,7 @@ export default class UserSettingsGeneralTab extends React.Component { const extraInfo = ( - {'Use Nickname for a name you might be called that is different from your first name and user name.'} - {'This is most often used when two or more people have similar sounding names and usernames.'} + {'Use Nickname for a name you might be called that is different from your first name and username. This is most often used when two or more people have similar sounding names and usernames.'} ); From 759ac7feee1ab4579c5186e5e599fc1d31efdc27 Mon Sep 17 00:00:00 2001 From: it33 Date: Fri, 9 Oct 2015 06:21:43 -0700 Subject: [PATCH 03/20] Adding link to "Fast, Obvious, Forgiving" --- doc/developer/Code-Contribution-Guidelines.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/developer/Code-Contribution-Guidelines.md b/doc/developer/Code-Contribution-Guidelines.md index 80676f107f..3bab47d23c 100644 --- a/doc/developer/Code-Contribution-Guidelines.md +++ b/doc/developer/Code-Contribution-Guidelines.md @@ -29,6 +29,9 @@ git checkout -b 2. Please make sure to thoroughly test your change before submitting a pull request. + Please review the ["Fast, Obvious, Forgiving" experience design principles](http://www.mattermost.org/design-principles/) for Mattermost and check that your feature meets the criteria. Also, for any changes to user interface or help text, please read the changes out loud, as a quick and easy way to catch any inconsitencies. + + ## Submitting a Pull Request 1. Please add yourself to the Mattermost [approved contributor list](https://docs.google.com/spreadsheets/d/1NTCeG-iL_VS9bFqtmHSfwETo5f-8MQ7oMDE5IUYJi_Y/pubhtml?gid=0&single=true) prior to submitting by completing the [contributor license agreement](http://www.mattermost.org/mattermost-contributor-agreement/). From 049662f9ae9c1a1c6f817ed02f9da925f2dd516c Mon Sep 17 00:00:00 2001 From: Christopher Speller Date: Fri, 9 Oct 2015 11:58:19 -0400 Subject: [PATCH 04/20] Turning on strict mode for mkdocs --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 12b209c847..103370288c 100644 --- a/Makefile +++ b/Makefile @@ -116,7 +116,7 @@ travis: rm $(DIST_PATH)/web/templates/*.bak mv doc/README.md doc/index.md - mkdocs build + mkdocs build --strict cp -r documentation-html $(DIST_PATH)/documentation-html tar -C dist -czf $(DIST_PATH).tar.gz mattermost From 61f92517f317f4b3f437b8db48f31c569ceb5f2d Mon Sep 17 00:00:00 2001 From: =Corey Hulen Date: Fri, 9 Oct 2015 12:24:39 -0700 Subject: [PATCH 05/20] PLT-586 fixing issues with security alert --- api/admin.go | 2 + config/config.json | 6 +-- docker/1.1/config_docker.json | 6 +-- docker/dev/config_docker.json | 6 +-- docker/local/config_docker.json | 6 +-- mattermost.go | 40 +++++++++++-------- model/config.go | 13 ++++-- store/sql_user_store.go | 21 ++++++++++ store/sql_user_store_test.go | 20 +++++++++- store/store.go | 1 + utils/config.go | 2 + utils/diagnostic.go | 17 ++++---- .../admin_console/privacy_settings.jsx | 34 ---------------- .../admin_console/service_settings.jsx | 37 ++++++++++++++++- 14 files changed, 136 insertions(+), 75 deletions(-) diff --git a/api/admin.go b/api/admin.go index 2167868e09..cd1e5d2dee 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 919737da7e..8ef1513503 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 ab5b0a7bec..653b6ffd74 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 ab5b0a7bec..653b6ffd74 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 ab5b0a7bec..653b6ffd74 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 6c0f0a1bfa..b8a44d26e5 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) - } - - 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.") diff --git a/model/config.go b/model/config.go index e4b99ad4a9..8a11b7bb72 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 011acd7e40..dc6b07a160 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 be21c8bd2b..874baf6347 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 1c4d08e360..e539bc98ae 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 44ee14a6eb..2c6f30bf07 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 da02e771be..7d13e0e52b 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 a32ca31365..70ec04f4a1 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, @@ -137,39 +136,6 @@ export default class PrivacySettings extends React.Component { -
- -
- - -

{'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.'}

-
-
-
{serverError} diff --git a/web/react/components/admin_console/service_settings.jsx b/web/react/components/admin_console/service_settings.jsx index 3968d9820d..f29d626461 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))) { @@ -304,6 +306,39 @@ export default class ServiceSettings extends React.Component {
+
+ +
+ + +

{'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.'}

+
+
+
{serverError} From 3a3b24e6be868bda6d80e5384f5bb09abe1f9b6e Mon Sep 17 00:00:00 2001 From: Reed Garmsen Date: Fri, 9 Oct 2015 13:44:20 -0700 Subject: [PATCH 06/20] Replaced change email verification email logo image with a static link --- api/templates/email_change_verify_body.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/templates/email_change_verify_body.html b/api/templates/email_change_verify_body.html index 1e1bcc22d7..a9b2a0741a 100644 --- a/api/templates/email_change_verify_body.html +++ b/api/templates/email_change_verify_body.html @@ -9,7 +9,7 @@ From e5a019390d91890bd098511f4845e27ff7bda430 Mon Sep 17 00:00:00 2001 From: hmhealey Date: Fri, 9 Oct 2015 16:45:40 -0400 Subject: [PATCH 07/20] Fixed download links in Safari --- api/file.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/file.go b/api/file.go index 9ebcd821be..4293475969 100644 --- a/api/file.go +++ b/api/file.go @@ -408,11 +408,11 @@ func getFile(c *Context, w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Length", strconv.Itoa(len(f))) w.Header().Del("Content-Type") // Content-Type will be set automatically by the http writer - // attach extra headers to trigger a download on IE and Edge + // attach extra headers to trigger a download on IE, Edge, and Safari ua := user_agent.New(r.UserAgent()) bname, _ := ua.Browser() - if bname == "Edge" || bname == "Internet Explorer" { + if bname == "Edge" || bname == "Internet Explorer" || bname == "Safari" { // trim off anything before the final / so we just get the file's name parts := strings.Split(filename, "/") From 2bfd6d9ac2071614437f8ac4e8586172994b6cc3 Mon Sep 17 00:00:00 2001 From: it33 Date: Sun, 11 Oct 2015 21:30:02 -0700 Subject: [PATCH 08/20] Update get_link_modal.jsx Removing out-of-date text saying that anyone can join a team from invite link, since there are now options to restrict sign-up based on email domain and SSO options --- web/react/components/get_link_modal.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/web/react/components/get_link_modal.jsx b/web/react/components/get_link_modal.jsx index 234013b93c..eb6bfa9b6c 100644 --- a/web/react/components/get_link_modal.jsx +++ b/web/react/components/get_link_modal.jsx @@ -96,7 +96,6 @@ export default class GetLinkModal extends React.Component {

Send teammates the link below for them to sign-up to this team site.

- Be careful not to share this link publicly, since anyone with the link can join your team.

- +