diff options
-rw-r--r-- | einterfaces/compliance.go | 23 | ||||
-rw-r--r-- | i18n/en.json | 21 | ||||
-rw-r--r-- | mattermost.go | 5 | ||||
-rw-r--r-- | model/compliance_post.go | 92 | ||||
-rw-r--r-- | model/compliance_post_test.go | 27 | ||||
-rw-r--r-- | model/system.go | 9 | ||||
-rw-r--r-- | store/sql_post_store.go | 56 | ||||
-rw-r--r-- | store/sql_post_store_test.go | 76 | ||||
-rw-r--r-- | store/store.go | 1 |
9 files changed, 306 insertions, 4 deletions
diff --git a/einterfaces/compliance.go b/einterfaces/compliance.go new file mode 100644 index 000000000..cd43152da --- /dev/null +++ b/einterfaces/compliance.go @@ -0,0 +1,23 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package einterfaces + +import ( + "github.com/mattermost/platform/model" +) + +type ComplianceInterface interface { + StartComplianceDailyJob() + RunComplianceJob(jobName string, dir string, filename string, startTime int64, endTime int64) *model.AppError +} + +var theComplianceInterface ComplianceInterface + +func RegisterComplianceInterface(newInterface ComplianceInterface) { + theComplianceInterface = newInterface +} + +func GetComplianceInterface() ComplianceInterface { + return theComplianceInterface +} diff --git a/i18n/en.json b/i18n/en.json index d16de7dbb..e42ade162 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -1796,6 +1796,23 @@ "translation": "Failed to read security bulletin details" }, { + "id": "ent.compliance.run_started.info", + "translation": "Compliance export started for job '{{.JobName}}' at '{{.FilePath}}'" + }, + { + "id": "ent.compliance.run_failed.error", + "translation": "Compliance export failed for job '{{.JobName}}' at '{{.FilePath}}'" + }, + { + "id": "ent.compliance.run_limit.warning", + "translation": "Compliance export warning for job '{{.JobName}}' too many rows returned truncating to 30,000 at '{{.FilePath}}'" + }, + { + "id": "ent.compliance.run_finished.info", + "translation": "Compliance export finished for job '{{.JobName}}' exported {{.Count}} records to '{{.FilePath}}'" + }, + + { "id": "mattermost.security_checks.debug", "translation": "Checking for security update from Mattermost" }, @@ -2792,6 +2809,10 @@ "translation": "We couldn't get post counts" }, { + "id": "store.sql_post.compliance_export.app_error", + "translation": "We couldn't get posts for compliance export" + }, + { "id": "store.sql_post.analytics_posts_count_by_day.app_error", "translation": "We couldn't get post counts by day" }, diff --git a/mattermost.go b/mattermost.go index 45ffcc88f..de97d36a2 100644 --- a/mattermost.go +++ b/mattermost.go @@ -19,6 +19,7 @@ import ( l4g "github.com/alecthomas/log4go" "github.com/mattermost/platform/api" + "github.com/mattermost/platform/einterfaces" "github.com/mattermost/platform/manualtesting" "github.com/mattermost/platform/model" "github.com/mattermost/platform/utils" @@ -91,6 +92,10 @@ func main() { setDiagnosticId() runSecurityAndDiagnosticsJobAndForget() + if einterfaces.GetComplianceInterface() != nil { + einterfaces.GetComplianceInterface().StartComplianceDailyJob() + } + // wait for kill signal before attempting to gracefully shutdown // the running service c := make(chan os.Signal) diff --git a/model/compliance_post.go b/model/compliance_post.go new file mode 100644 index 000000000..636be8f17 --- /dev/null +++ b/model/compliance_post.go @@ -0,0 +1,92 @@ +// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "time" +) + +type CompliancePost struct { + + // From Team + TeamName string + TeamDisplayName string + + // From Channel + ChannelName string + ChannelDisplayName string + + // From User + UserUsername string + UserEmail string + UserNickname string + + // From Post + PostId string + PostCreateAt int64 + PostUpdateAt int64 + PostDeleteAt int64 + PostRootId string + PostParentId string + PostOriginalId string + PostMessage string + PostType string + PostProps string + PostHashtags string + PostFilenames string +} + +func CompliancePostHeader() []string { + return []string{ + "TeamName", + "TeamDisplayName", + + "ChannelName", + "ChannelDisplayName", + + "UserUsername", + "UserEmail", + "UserNickname", + + "PostId", + "PostCreateAt", + "PostUpdateAt", + "PostDeleteAt", + "PostRootId", + "PostParentId", + "PostOriginalId", + "PostMessage", + "PostType", + "PostProps", + "PostHashtags", + "PostFilenames", + } +} + +func (me *CompliancePost) Row() []string { + return []string{ + me.TeamName, + me.TeamDisplayName, + + me.ChannelName, + me.ChannelDisplayName, + + me.UserUsername, + me.UserEmail, + me.UserNickname, + + me.PostId, + time.Unix(0, me.PostCreateAt*1000).Format(time.RFC3339), + time.Unix(0, me.PostUpdateAt*1000).Format(time.RFC3339), + time.Unix(0, me.PostDeleteAt*1000).Format(time.RFC3339), + me.PostRootId, + me.PostParentId, + me.PostOriginalId, + me.PostMessage, + me.PostType, + me.PostProps, + me.PostHashtags, + me.PostFilenames, + } +} diff --git a/model/compliance_post_test.go b/model/compliance_post_test.go new file mode 100644 index 000000000..28e20ba4b --- /dev/null +++ b/model/compliance_post_test.go @@ -0,0 +1,27 @@ +// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "testing" +) + +func TestCompliancePostHeader(t *testing.T) { + if CompliancePostHeader()[0] != "TeamName" { + t.Fatal() + } +} + +func TestCompliancePost(t *testing.T) { + o := CompliancePost{TeamName: "test", PostFilenames: "files", PostCreateAt: GetMillis()} + r := o.Row() + + if r[0] != "test" { + t.Fatal() + } + + if r[len(r)-1] != "files" { + t.Fatal() + } +} diff --git a/model/system.go b/model/system.go index b387749f6..68d542c15 100644 --- a/model/system.go +++ b/model/system.go @@ -9,10 +9,11 @@ import ( ) const ( - SYSTEM_DIAGNOSTIC_ID = "DiagnosticId" - SYSTEM_RAN_UNIT_TESTS = "RanUnitTests" - SYSTEM_LAST_SECURITY_TIME = "LastSecurityTime" - SYSTEM_ACTIVE_LICENSE_ID = "ActiveLicenseId" + SYSTEM_DIAGNOSTIC_ID = "DiagnosticId" + SYSTEM_RAN_UNIT_TESTS = "RanUnitTests" + SYSTEM_LAST_SECURITY_TIME = "LastSecurityTime" + SYSTEM_ACTIVE_LICENSE_ID = "ActiveLicenseId" + SYSTEM_LAST_COMPLIANCE_TIME = "LastComplianceTime" ) type System struct { diff --git a/store/sql_post_store.go b/store/sql_post_store.go index 3346534ab..198347ff2 100644 --- a/store/sql_post_store.go +++ b/store/sql_post_store.go @@ -979,3 +979,59 @@ func (s SqlPostStore) AnalyticsPostCount(teamId string, mustHaveFile bool, mustH return storeChannel } + +func (s SqlPostStore) ComplianceExport(startTime int64, endTime int64) StoreChannel { + storeChannel := make(StoreChannel) + + go func() { + result := StoreResult{} + + query := + `SELECT + Teams.Name AS TeamName, + Teams.DisplayName AS TeamDisplayName, + Channels.Name AS ChannelName, + Channels.DisplayName AS ChannelDisplayName, + Users.Username AS UserUsername, + Users.Email AS UserEmail, + Users.Nickname AS UserNickname, + Posts.Id AS PostId, + Posts.CreateAt AS PostCreateAt, + Posts.UpdateAt AS PostUpdateAt, + Posts.DeleteAt AS PostDeleteAt, + Posts.RootId AS PostRootId, + Posts.ParentId AS PostParentId, + Posts.OriginalId AS PostOriginalId, + Posts.Message AS PostMessage, + Posts.Type AS PostType, + Posts.Props AS PostProps, + Posts.Hashtags AS PostHashtags, + Posts.Filenames AS PostFilenames + FROM + Teams, + Channels, + Users, + Posts + WHERE + Teams.Id = Channels.TeamId + AND Posts.ChannelId = Channels.Id + AND Posts.UserId = Users.Id + AND Posts.CreateAt > :StartTime + AND Posts.CreateAt <= :EndTime + ORDER BY Posts.CreateAt + LIMIT 30000` + + var cposts []*model.CompliancePost + + if _, err := s.GetReplica().Select(&cposts, query, map[string]interface{}{"StartTime": startTime, "EndTime": endTime}); err != nil { + result.Err = model.NewLocAppError("SqlPostStore.ComplianceExport", "store.sql_post.compliance_export.app_error", nil, err.Error()) + } else { + result.Data = cposts + } + + storeChannel <- result + close(storeChannel) + }() + + return storeChannel +} diff --git a/store/sql_post_store_test.go b/store/sql_post_store_test.go index d69f7906c..512c27ee4 100644 --- a/store/sql_post_store_test.go +++ b/store/sql_post_store_test.go @@ -895,3 +895,79 @@ func TestPostCountsByDay(t *testing.T) { } } } + +func TestComplianceExport(t *testing.T) { + Setup() + + time.Sleep(100 * time.Millisecond) + + t1 := &model.Team{} + t1.DisplayName = "DisplayName" + t1.Name = "a" + model.NewId() + "b" + t1.Email = model.NewId() + "@nowhere.com" + t1.Type = model.TEAM_OPEN + t1 = Must(store.Team().Save(t1)).(*model.Team) + + u1 := &model.User{} + u1.TeamId = t1.Id + u1.Email = model.NewId() + u1.Username = model.NewId() + u1 = Must(store.User().Save(u1)).(*model.User) + + c1 := &model.Channel{} + c1.TeamId = t1.Id + c1.DisplayName = "Channel2" + c1.Name = "a" + model.NewId() + "b" + c1.Type = model.CHANNEL_OPEN + c1 = Must(store.Channel().Save(c1)).(*model.Channel) + + o1 := &model.Post{} + o1.ChannelId = c1.Id + o1.UserId = u1.Id + o1.CreateAt = model.GetMillis() + o1.Message = "a" + model.NewId() + "b" + o1 = Must(store.Post().Save(o1)).(*model.Post) + + o1a := &model.Post{} + o1a.ChannelId = c1.Id + o1a.UserId = u1.Id + o1a.CreateAt = o1.CreateAt + 10 + o1a.Message = "a" + model.NewId() + "b" + o1a = Must(store.Post().Save(o1a)).(*model.Post) + + o2 := &model.Post{} + o2.ChannelId = c1.Id + o2.UserId = u1.Id + o2.CreateAt = o1.CreateAt + 20 + o2.Message = "a" + model.NewId() + "b" + o2 = Must(store.Post().Save(o2)).(*model.Post) + + o2a := &model.Post{} + o2a.ChannelId = c1.Id + o2a.UserId = u1.Id + o2a.CreateAt = o1.CreateAt + 30 + o2a.Message = "a" + model.NewId() + "b" + o2a = Must(store.Post().Save(o2a)).(*model.Post) + + time.Sleep(100 * time.Millisecond) + + if r1 := <-store.Post().ComplianceExport(o1.CreateAt-1, o2a.CreateAt+1); r1.Err != nil { + t.Fatal(r1.Err) + } else { + cposts := r1.Data.([]*model.CompliancePost) + t.Log(cposts) + + if len(cposts) != 4 { + t.Fatal("return wrong results length") + } + + if cposts[0].PostId != o1.Id { + t.Fatal("Wrong sort") + } + + if cposts[3].PostId != o2a.Id { + t.Fatal("Wrong sort") + } + + } +} diff --git a/store/store.go b/store/store.go index b041cfa25..7aef18203 100644 --- a/store/store.go +++ b/store/store.go @@ -105,6 +105,7 @@ type PostStore interface { AnalyticsUserCountsWithPostsByDay(teamId string) StoreChannel AnalyticsPostCountsByDay(teamId string) StoreChannel AnalyticsPostCount(teamId string, mustHaveFile bool, mustHaveHashtag bool) StoreChannel + ComplianceExport(startTime int64, endTime int64) StoreChannel } type UserStore interface { |