From d3e934d07ac0a58a24a435ea7c5b3bd222ef509a Mon Sep 17 00:00:00 2001 From: Jonathan Date: Wed, 7 Feb 2018 09:02:46 -0500 Subject: XYZ-35: Added Support for GlobalRelay Compliance Export Format * Added username to ChannelMemberHistory struct in anticipation of supporting GlobalRelay in Compliance Export * Removed translation from debug output - this makes it complicated to use utils functions from tests in the enterprise repo * Added an advanced email function that allows for greater control over message details. Updated MessageExport config to support GlobalRelay. Added attachment support to InBucket unit tests * Moving templates in from enterprise to solve test issues * Added export format to diagnostics * Changed email attachment code to use FileBackend so that S3 storage is properly supported --- model/channel_member_history.go | 5 +++- model/config.go | 21 +++++++++++++++++ model/config_test.go | 52 ++++++++++++++++++++++++++++++++++++++++- model/message_export.go | 1 + 4 files changed, 77 insertions(+), 2 deletions(-) (limited to 'model') diff --git a/model/channel_member_history.go b/model/channel_member_history.go index bc71b580a..47c59d54e 100644 --- a/model/channel_member_history.go +++ b/model/channel_member_history.go @@ -6,7 +6,10 @@ package model type ChannelMemberHistory struct { ChannelId string UserId string - UserEmail string `db:"Email"` JoinTime int64 LeaveTime *int64 + + // these two fields are never set in the database - when we SELECT, we join on Users to get them + UserEmail string `db:"Email"` + Username string } diff --git a/model/config.go b/model/config.go index 20011f7cb..9010eaeae 100644 --- a/model/config.go +++ b/model/config.go @@ -158,6 +158,9 @@ const ( PLUGIN_SETTINGS_DEFAULT_DIRECTORY = "./plugins" PLUGIN_SETTINGS_DEFAULT_CLIENT_DIRECTORY = "./client/plugins" + + COMPLIANCE_EXPORT_TYPE_ACTIANCE = "actiance" + COMPLIANCE_EXPORT_TYPE_GLOBALRELAY = "globalrelay" ) type ServiceSettings struct { @@ -1623,9 +1626,13 @@ func (s *PluginSettings) SetDefaults() { type MessageExportSettings struct { EnableExport *bool + ExportFormat *string DailyRunTime *string ExportFromTimestamp *int64 BatchSize *int + + // formatter-specific settings - these are only expected to be non-nil if ExportFormat is set to the associated format + GlobalRelayEmailAddress *string } func (s *MessageExportSettings) SetDefaults() { @@ -1633,6 +1640,10 @@ func (s *MessageExportSettings) SetDefaults() { s.EnableExport = NewBool(false) } + if s.ExportFormat == nil { + s.ExportFormat = NewString(COMPLIANCE_EXPORT_TYPE_ACTIANCE) + } + if s.DailyRunTime == nil { s.DailyRunTime = NewString("01:00") } @@ -2170,6 +2181,16 @@ func (mes *MessageExportSettings) isValid(fs FileSettings) *AppError { return NewAppError("Config.IsValid", "model.config.is_valid.message_export.daily_runtime.app_error", nil, err.Error(), http.StatusBadRequest) } else if mes.BatchSize == nil || *mes.BatchSize < 0 { return NewAppError("Config.IsValid", "model.config.is_valid.message_export.batch_size.app_error", nil, "", http.StatusBadRequest) + } else if mes.ExportFormat == nil || (*mes.ExportFormat != COMPLIANCE_EXPORT_TYPE_ACTIANCE && *mes.ExportFormat != COMPLIANCE_EXPORT_TYPE_GLOBALRELAY) { + return NewAppError("Config.IsValid", "model.config.is_valid.message_export.export_type.app_error", nil, "", http.StatusBadRequest) + } + + if *mes.ExportFormat == COMPLIANCE_EXPORT_TYPE_GLOBALRELAY { + // validating email addresses is hard - just make sure it contains an '@' sign + // see https://stackoverflow.com/questions/201323/using-a-regular-expression-to-validate-an-email-address + if mes.GlobalRelayEmailAddress == nil || !strings.Contains(*mes.GlobalRelayEmailAddress, "@") { + return NewAppError("Config.IsValid", "model.config.is_valid.message_export.global_relay_email_address.app_error", nil, "", http.StatusBadRequest) + } } } return nil diff --git a/model/config_test.go b/model/config_test.go index 5510c40d0..919f73fd7 100644 --- a/model/config_test.go +++ b/model/config_test.go @@ -136,7 +136,7 @@ func TestMessageExportSettingsIsValidBatchSizeInvalid(t *testing.T) { require.Error(t, mes.isValid(*fs)) } -func TestMessageExportSettingsIsValid(t *testing.T) { +func TestMessageExportSettingsIsValidExportFormatInvalid(t *testing.T) { fs := &FileSettings{ DriverName: NewString("foo"), // bypass file location check } @@ -147,6 +147,55 @@ func TestMessageExportSettingsIsValid(t *testing.T) { BatchSize: NewInt(100), } + // should fail fast because export format isn't set + require.Error(t, mes.isValid(*fs)) +} + +func TestMessageExportSettingsIsValidGlobalRelayEmailAddressInvalid(t *testing.T) { + fs := &FileSettings{ + DriverName: NewString("foo"), // bypass file location check + } + mes := &MessageExportSettings{ + EnableExport: NewBool(true), + ExportFormat: NewString(COMPLIANCE_EXPORT_TYPE_GLOBALRELAY), + ExportFromTimestamp: NewInt64(0), + DailyRunTime: NewString("15:04"), + BatchSize: NewInt(100), + } + + // should fail fast because global relay email address isn't set + require.Error(t, mes.isValid(*fs)) +} + +func TestMessageExportSettingsIsValidActiance(t *testing.T) { + fs := &FileSettings{ + DriverName: NewString("foo"), // bypass file location check + } + mes := &MessageExportSettings{ + EnableExport: NewBool(true), + ExportFormat: NewString(COMPLIANCE_EXPORT_TYPE_ACTIANCE), + ExportFromTimestamp: NewInt64(0), + DailyRunTime: NewString("15:04"), + BatchSize: NewInt(100), + } + + // should pass because everything is valid + require.Nil(t, mes.isValid(*fs)) +} + +func TestMessageExportSettingsIsValidGlobalRelay(t *testing.T) { + fs := &FileSettings{ + DriverName: NewString("foo"), // bypass file location check + } + mes := &MessageExportSettings{ + EnableExport: NewBool(true), + ExportFormat: NewString(COMPLIANCE_EXPORT_TYPE_GLOBALRELAY), + ExportFromTimestamp: NewInt64(0), + DailyRunTime: NewString("15:04"), + BatchSize: NewInt(100), + GlobalRelayEmailAddress: NewString("test@mattermost.com"), + } + // should pass because everything is valid require.Nil(t, mes.isValid(*fs)) } @@ -159,6 +208,7 @@ func TestMessageExportSetDefaults(t *testing.T) { require.Equal(t, "01:00", *mes.DailyRunTime) require.Equal(t, int64(0), *mes.ExportFromTimestamp) require.Equal(t, 10000, *mes.BatchSize) + require.Equal(t, COMPLIANCE_EXPORT_TYPE_ACTIANCE, *mes.ExportFormat) } func TestMessageExportSetDefaultsExportEnabledExportFromTimestampNil(t *testing.T) { diff --git a/model/message_export.go b/model/message_export.go index b59b114d4..22641deee 100644 --- a/model/message_export.go +++ b/model/message_export.go @@ -9,6 +9,7 @@ type MessageExport struct { UserId *string UserEmail *string + Username *string PostId *string PostCreateAt *int64 -- cgit v1.2.3-1-g7c22 From 7bd298ceaa24c0721e0acd65692cb2d1ca4983f3 Mon Sep 17 00:00:00 2001 From: Vordimous Date: Wed, 7 Feb 2018 09:17:18 -0500 Subject: PLT-7537: Move channel CLI command posts system message to channel. (#8161) * [PTL-7537] implement feature and test * [PTL-7537] Update feature to post the the room requiring a username flag to be used * [PTL-7537] update tests with username * update test to remove changes to the test helper struct * use the basic team and user --- model/post.go | 2 ++ 1 file changed, 2 insertions(+) (limited to 'model') diff --git a/model/post.go b/model/post.go index 391b948f4..7cf0f1b35 100644 --- a/model/post.go +++ b/model/post.go @@ -28,6 +28,7 @@ const ( POST_ADD_REMOVE = "system_add_remove" // Deprecated, use POST_ADD_TO_CHANNEL or POST_REMOVE_FROM_CHANNEL instead POST_ADD_TO_CHANNEL = "system_add_to_channel" POST_REMOVE_FROM_CHANNEL = "system_remove_from_channel" + POST_MOVE_CHANNEL = "system_move_channel" POST_ADD_TO_TEAM = "system_add_to_team" POST_REMOVE_FROM_TEAM = "system_remove_from_team" POST_HEADER_CHANGE = "system_header_change" @@ -196,6 +197,7 @@ func (o *Post) IsValid() *AppError { POST_LEAVE_TEAM, POST_ADD_TO_CHANNEL, POST_REMOVE_FROM_CHANNEL, + POST_MOVE_CHANNEL, POST_ADD_TO_TEAM, POST_REMOVE_FROM_TEAM, POST_SLACK_ATTACHMENT, -- cgit v1.2.3-1-g7c22 From eff65aa05c74e93533c2504b8141b0474011e68c Mon Sep 17 00:00:00 2001 From: Chris Date: Wed, 7 Feb 2018 11:05:46 -0600 Subject: ABC-132: sign error page parameters (#8197) * sign error page parameters * add comments --- model/system.go | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) (limited to 'model') diff --git a/model/system.go b/model/system.go index 020c50858..2a636b14f 100644 --- a/model/system.go +++ b/model/system.go @@ -6,14 +6,16 @@ package model import ( "encoding/json" "io" + "math/big" ) const ( - SYSTEM_DIAGNOSTIC_ID = "DiagnosticId" - SYSTEM_RAN_UNIT_TESTS = "RanUnitTests" - SYSTEM_LAST_SECURITY_TIME = "LastSecurityTime" - SYSTEM_ACTIVE_LICENSE_ID = "ActiveLicenseId" - SYSTEM_LAST_COMPLIANCE_TIME = "LastComplianceTime" + SYSTEM_DIAGNOSTIC_ID = "DiagnosticId" + SYSTEM_RAN_UNIT_TESTS = "RanUnitTests" + SYSTEM_LAST_SECURITY_TIME = "LastSecurityTime" + SYSTEM_ACTIVE_LICENSE_ID = "ActiveLicenseId" + SYSTEM_LAST_COMPLIANCE_TIME = "LastComplianceTime" + SYSTEM_ASYMMETRIC_SIGNING_KEY = "AsymmetricSigningKey" ) type System struct { @@ -31,3 +33,14 @@ func SystemFromJson(data io.Reader) *System { json.NewDecoder(data).Decode(&o) return o } + +type SystemAsymmetricSigningKey struct { + ECDSAKey *SystemECDSAKey `json:"ecdsa_key,omitempty"` +} + +type SystemECDSAKey struct { + Curve string `json:"curve"` + X *big.Int `json:"x"` + Y *big.Int `json:"y"` + D *big.Int `json:"d,omitempty"` +} -- cgit v1.2.3-1-g7c22 From 0f703a3368a0b16fcd48b474377f0dbd2144f366 Mon Sep 17 00:00:00 2001 From: Chris Date: Wed, 7 Feb 2018 16:20:51 -0600 Subject: Eliminate utils.SetLicense calls (#8217) * eliminate utils.SetLicense calls * test fix * another test fix * more test fixes --- model/license.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'model') diff --git a/model/license.go b/model/license.go index f96cba06c..942a18d55 100644 --- a/model/license.go +++ b/model/license.go @@ -173,6 +173,25 @@ func (l *License) ToJson() string { return string(b) } +// NewTestLicense returns a license that expires in the future and has the given features. +func NewTestLicense(features ...string) *License { + ret := &License{ + ExpiresAt: GetMillis() + 90*24*60*60*1000, + Customer: &Customer{}, + Features: &Features{}, + } + ret.Features.SetDefaults() + + featureMap := map[string]bool{} + for _, feature := range features { + featureMap[feature] = true + } + featureJson, _ := json.Marshal(featureMap) + json.Unmarshal(featureJson, &ret.Features) + + return ret +} + func LicenseFromJson(data io.Reader) *License { var o *License json.NewDecoder(data).Decode(&o) -- cgit v1.2.3-1-g7c22 From cf929476bdaa9a388fdfde62889bbef2a4dbf1c2 Mon Sep 17 00:00:00 2001 From: Chris Date: Thu, 8 Feb 2018 10:54:45 -0600 Subject: fix client4 post sanitization (#8219) --- model/client4.go | 4 ++-- model/client4_test.go | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++ model/post.go | 13 ++++++------ 3 files changed, 67 insertions(+), 8 deletions(-) create mode 100644 model/client4_test.go (limited to 'model') diff --git a/model/client4.go b/model/client4.go index 0694ecbdf..962b816bb 100644 --- a/model/client4.go +++ b/model/client4.go @@ -1729,7 +1729,7 @@ func (c *Client4) RemoveUserFromChannel(channelId, userId string) (bool, *Respon // CreatePost creates a post based on the provided post struct. func (c *Client4) CreatePost(post *Post) (*Post, *Response) { - if r, err := c.DoApiPost(c.GetPostsRoute(), post.ToJson()); err != nil { + if r, err := c.DoApiPost(c.GetPostsRoute(), post.ToUnsanitizedJson()); err != nil { return nil, BuildErrorResponse(r, err) } else { defer closeBody(r) @@ -1739,7 +1739,7 @@ func (c *Client4) CreatePost(post *Post) (*Post, *Response) { // UpdatePost updates a post based on the provided post struct. func (c *Client4) UpdatePost(postId string, post *Post) (*Post, *Response) { - if r, err := c.DoApiPut(c.GetPostRoute(postId), post.ToJson()); err != nil { + if r, err := c.DoApiPut(c.GetPostRoute(postId), post.ToUnsanitizedJson()); err != nil { return nil, BuildErrorResponse(r, err) } else { defer closeBody(r) diff --git a/model/client4_test.go b/model/client4_test.go new file mode 100644 index 000000000..f7923fa8f --- /dev/null +++ b/model/client4_test.go @@ -0,0 +1,58 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" +) + +// https://github.com/mattermost/mattermost-server/issues/8205 +func TestClient4CreatePost(t *testing.T) { + post := &Post{ + Props: map[string]interface{}{ + "attachments": []*SlackAttachment{ + &SlackAttachment{ + Actions: []*PostAction{ + &PostAction{ + Integration: &PostActionIntegration{ + Context: map[string]interface{}{ + "foo": "bar", + }, + URL: "http://foo.com", + }, + Name: "Foo", + }, + }, + }, + }, + }, + } + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + attachments := PostFromJson(r.Body).Attachments() + assert.Equal(t, []*SlackAttachment{ + &SlackAttachment{ + Actions: []*PostAction{ + &PostAction{ + Integration: &PostActionIntegration{ + Context: map[string]interface{}{ + "foo": "bar", + }, + URL: "http://foo.com", + }, + Name: "Foo", + }, + }, + }, + }, attachments) + })) + + client := NewAPIv4Client(server.URL) + _, resp := client.CreatePost(post) + assert.Equal(t, http.StatusOK, resp.StatusCode) +} diff --git a/model/post.go b/model/post.go index 7cf0f1b35..4a774b5d4 100644 --- a/model/post.go +++ b/model/post.go @@ -122,12 +122,13 @@ type PostActionIntegrationResponse struct { func (o *Post) ToJson() string { copy := *o copy.StripActionIntegrations() - b, err := json.Marshal(©) - if err != nil { - return "" - } else { - return string(b) - } + b, _ := json.Marshal(©) + return string(b) +} + +func (o *Post) ToUnsanitizedJson() string { + b, _ := json.Marshal(o) + return string(b) } func PostFromJson(data io.Reader) *Post { -- cgit v1.2.3-1-g7c22