PLT-2115 Adding compliance
Этот коммит содержится в:
112
api/admin.go
112
api/admin.go
@@ -5,15 +5,18 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
l4g "github.com/alecthomas/log4go"
|
l4g "github.com/alecthomas/log4go"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/mattermost/platform/einterfaces"
|
||||||
"github.com/mattermost/platform/model"
|
"github.com/mattermost/platform/model"
|
||||||
"github.com/mattermost/platform/utils"
|
"github.com/mattermost/platform/utils"
|
||||||
|
"github.com/mssola/user_agent"
|
||||||
)
|
)
|
||||||
|
|
||||||
func InitAdmin(r *mux.Router) {
|
func InitAdmin(r *mux.Router) {
|
||||||
@@ -27,8 +30,11 @@ func InitAdmin(r *mux.Router) {
|
|||||||
sr.Handle("/test_email", ApiUserRequired(testEmail)).Methods("POST")
|
sr.Handle("/test_email", ApiUserRequired(testEmail)).Methods("POST")
|
||||||
sr.Handle("/client_props", ApiAppHandler(getClientConfig)).Methods("GET")
|
sr.Handle("/client_props", ApiAppHandler(getClientConfig)).Methods("GET")
|
||||||
sr.Handle("/log_client", ApiAppHandler(logClient)).Methods("POST")
|
sr.Handle("/log_client", ApiAppHandler(logClient)).Methods("POST")
|
||||||
sr.Handle("/analytics/{id:[A-Za-z0-9]+}/{name:[A-Za-z0-9_]+}", ApiAppHandler(getAnalytics)).Methods("GET")
|
sr.Handle("/analytics/{id:[A-Za-z0-9]+}/{name:[A-Za-z0-9_]+}", ApiUserRequired(getAnalytics)).Methods("GET")
|
||||||
sr.Handle("/analytics/{name:[A-Za-z0-9_]+}", ApiAppHandler(getAnalytics)).Methods("GET")
|
sr.Handle("/analytics/{name:[A-Za-z0-9_]+}", ApiUserRequired(getAnalytics)).Methods("GET")
|
||||||
|
sr.Handle("/save_compliance_report", ApiUserRequired(saveComplianceReport)).Methods("POST")
|
||||||
|
sr.Handle("/compliance_reports", ApiUserRequired(getComplianceReports)).Methods("GET")
|
||||||
|
sr.Handle("/download_compliance_report/{id:[A-Za-z0-9]+}", ApiUserRequired(downloadComplianceReport)).Methods("GET")
|
||||||
}
|
}
|
||||||
|
|
||||||
func getLogs(c *Context, w http.ResponseWriter, r *http.Request) {
|
func getLogs(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -142,6 +148,8 @@ func saveConfig(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.LogAudit("")
|
||||||
|
|
||||||
utils.SaveConfig(utils.CfgFileName, cfg)
|
utils.SaveConfig(utils.CfgFileName, cfg)
|
||||||
utils.LoadConfig(utils.CfgFileName)
|
utils.LoadConfig(utils.CfgFileName)
|
||||||
json := utils.Cfg.ToJson()
|
json := utils.Cfg.ToJson()
|
||||||
@@ -174,6 +182,104 @@ func testEmail(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
w.Write([]byte(model.MapToJson(m)))
|
w.Write([]byte(model.MapToJson(m)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getComplianceReports(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
|
if !c.HasSystemAdminPermissions("getComplianceReports") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !*utils.Cfg.ComplianceSettings.Enable || !utils.IsLicensed || !*utils.License.Features.Compliance {
|
||||||
|
c.Err = model.NewLocAppError("getComplianceReports", "ent.compliance.licence_disable.app_error", nil, "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if result := <-Srv.Store.Compliance().GetAll(); result.Err != nil {
|
||||||
|
c.Err = result.Err
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
crs := result.Data.(model.Compliances)
|
||||||
|
w.Write([]byte(crs.ToJson()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func saveComplianceReport(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
|
if !c.HasSystemAdminPermissions("getComplianceReports") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !*utils.Cfg.ComplianceSettings.Enable || !utils.IsLicensed || !*utils.License.Features.Compliance || einterfaces.GetComplianceInterface() == nil {
|
||||||
|
c.Err = model.NewLocAppError("saveComplianceReport", "ent.compliance.licence_disable.app_error", nil, "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
job := model.ComplianceFromJson(r.Body)
|
||||||
|
if job == nil {
|
||||||
|
c.SetInvalidParam("saveComplianceReport", "compliance")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
job.UserId = c.Session.UserId
|
||||||
|
job.Type = model.COMPLIANCE_TYPE_ADHOC
|
||||||
|
|
||||||
|
if result := <-Srv.Store.Compliance().Save(job); result.Err != nil {
|
||||||
|
c.Err = result.Err
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
job = result.Data.(*model.Compliance)
|
||||||
|
go einterfaces.GetComplianceInterface().RunComplianceJob(job)
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Write([]byte(job.ToJson()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func downloadComplianceReport(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
|
if !c.HasSystemAdminPermissions("downloadComplianceReport") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !*utils.Cfg.ComplianceSettings.Enable || !utils.IsLicensed || !*utils.License.Features.Compliance || einterfaces.GetComplianceInterface() == nil {
|
||||||
|
c.Err = model.NewLocAppError("downloadComplianceReport", "ent.compliance.licence_disable.app_error", nil, "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
params := mux.Vars(r)
|
||||||
|
|
||||||
|
id := params["id"]
|
||||||
|
if len(id) != 26 {
|
||||||
|
c.SetInvalidParam("downloadComplianceReport", "id")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if result := <-Srv.Store.Compliance().Get(id); result.Err != nil {
|
||||||
|
c.Err = result.Err
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
job := result.Data.(*model.Compliance)
|
||||||
|
c.LogAudit("downloaded " + job.JobName())
|
||||||
|
|
||||||
|
if f, err := ioutil.ReadFile(*utils.Cfg.ComplianceSettings.Directory + "compliance/" + job.JobName() + ".zip"); err != nil {
|
||||||
|
c.Err = model.NewLocAppError("readFile", "api.file.read_file.reading_local.app_error", nil, err.Error())
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
w.Header().Set("Cache-Control", "max-age=2592000, public")
|
||||||
|
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, Edge, and Safari
|
||||||
|
ua := user_agent.New(r.UserAgent())
|
||||||
|
bname, _ := ua.Browser()
|
||||||
|
|
||||||
|
w.Header().Set("Content-Disposition", "attachment;filename=\""+job.JobName()+".zip\"")
|
||||||
|
|
||||||
|
if bname == "Edge" || bname == "Internet Explorer" || bname == "Safari" {
|
||||||
|
// trim off anything before the final / so we just get the file's name
|
||||||
|
w.Header().Set("Content-Type", "application/octet-stream")
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Write(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func getAnalytics(c *Context, w http.ResponseWriter, r *http.Request) {
|
func getAnalytics(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
if !c.HasSystemAdminPermissions("getAnalytics") {
|
if !c.HasSystemAdminPermissions("getAnalytics") {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -4,11 +4,10 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/mattermost/platform/model"
|
"github.com/mattermost/platform/model"
|
||||||
"github.com/mattermost/platform/store"
|
"github.com/mattermost/platform/store"
|
||||||
"github.com/mattermost/platform/utils"
|
"github.com/mattermost/platform/utils"
|
||||||
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetLogs(t *testing.T) {
|
func TestGetLogs(t *testing.T) {
|
||||||
|
|||||||
@@ -23,6 +23,26 @@ func InitLicense(r *mux.Router) {
|
|||||||
sr.Handle("/client_config", ApiAppHandler(getClientLicenceConfig)).Methods("GET")
|
sr.Handle("/client_config", ApiAppHandler(getClientLicenceConfig)).Methods("GET")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func LoadLicense() {
|
||||||
|
licenseId := ""
|
||||||
|
if result := <-Srv.Store.System().Get(); result.Err == nil {
|
||||||
|
props := result.Data.(model.StringMap)
|
||||||
|
licenseId = props[model.SYSTEM_ACTIVE_LICENSE_ID]
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(licenseId) != 26 {
|
||||||
|
l4g.Warn(utils.T("mattermost.load_license.find.warn"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if result := <-Srv.Store.License().Get(licenseId); result.Err == nil {
|
||||||
|
record := result.Data.(*model.LicenseRecord)
|
||||||
|
utils.LoadLicense([]byte(record.Bytes))
|
||||||
|
} else {
|
||||||
|
l4g.Warn(utils.T("mattermost.load_license.find.warn"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func addLicense(c *Context, w http.ResponseWriter, r *http.Request) {
|
func addLicense(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
c.LogAudit("attempt")
|
c.LogAudit("attempt")
|
||||||
err := r.ParseMultipartForm(model.MAX_FILE_SIZE)
|
err := r.ParseMultipartForm(model.MAX_FILE_SIZE)
|
||||||
|
|||||||
@@ -19,7 +19,9 @@
|
|||||||
"SessionLengthWebInDays": 30,
|
"SessionLengthWebInDays": 30,
|
||||||
"SessionLengthMobileInDays": 30,
|
"SessionLengthMobileInDays": 30,
|
||||||
"SessionLengthSSOInDays": 30,
|
"SessionLengthSSOInDays": 30,
|
||||||
"SessionCacheInMinutes": 10
|
"SessionCacheInMinutes": 10,
|
||||||
|
"WebsocketSecurePort": 443,
|
||||||
|
"WebsocketPort": 80
|
||||||
},
|
},
|
||||||
"TeamSettings": {
|
"TeamSettings": {
|
||||||
"SiteName": "Mattermost",
|
"SiteName": "Mattermost",
|
||||||
@@ -113,5 +115,33 @@
|
|||||||
"AuthEndpoint": "",
|
"AuthEndpoint": "",
|
||||||
"TokenEndpoint": "",
|
"TokenEndpoint": "",
|
||||||
"UserApiEndpoint": ""
|
"UserApiEndpoint": ""
|
||||||
|
},
|
||||||
|
"GoogleSettings": {
|
||||||
|
"Enable": false,
|
||||||
|
"Secret": "",
|
||||||
|
"Id": "",
|
||||||
|
"Scope": "",
|
||||||
|
"AuthEndpoint": "",
|
||||||
|
"TokenEndpoint": "",
|
||||||
|
"UserApiEndpoint": ""
|
||||||
|
},
|
||||||
|
"LdapSettings": {
|
||||||
|
"Enable": false,
|
||||||
|
"LdapServer": "",
|
||||||
|
"LdapPort": 389,
|
||||||
|
"BaseDN": "",
|
||||||
|
"BindUsername": "",
|
||||||
|
"BindPassword": "",
|
||||||
|
"FirstNameAttribute": "",
|
||||||
|
"LastNameAttribute": "",
|
||||||
|
"EmailAttribute": "",
|
||||||
|
"UsernameAttribute": "",
|
||||||
|
"IdAttribute": "",
|
||||||
|
"QueryTimeout": 60
|
||||||
|
},
|
||||||
|
"ComplianceSettings": {
|
||||||
|
"Enable": true,
|
||||||
|
"Directory": "./data/",
|
||||||
|
"EnableDaily": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
|
|
||||||
type ComplianceInterface interface {
|
type ComplianceInterface interface {
|
||||||
StartComplianceDailyJob()
|
StartComplianceDailyJob()
|
||||||
RunComplianceJob(jobName string, dir string, filename string, startTime int64, endTime int64) *model.AppError
|
RunComplianceJob(job *model.Compliance) *model.AppError
|
||||||
}
|
}
|
||||||
|
|
||||||
var theComplianceInterface ComplianceInterface
|
var theComplianceInterface ComplianceInterface
|
||||||
|
|||||||
37
i18n/en.json
37
i18n/en.json
@@ -1811,7 +1811,10 @@
|
|||||||
"id": "ent.compliance.run_finished.info",
|
"id": "ent.compliance.run_finished.info",
|
||||||
"translation": "Compliance export finished for job '{{.JobName}}' exported {{.Count}} records to '{{.FilePath}}'"
|
"translation": "Compliance export finished for job '{{.JobName}}' exported {{.Count}} records to '{{.FilePath}}'"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "ent.compliance.licence_disable.app_error",
|
||||||
|
"translation": "Compliance functionality disabled by current license. Please contact your system administrator about upgrading your enterprise license."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "mattermost.security_checks.debug",
|
"id": "mattermost.security_checks.debug",
|
||||||
"translation": "Checking for security update from Mattermost"
|
"translation": "Checking for security update from Mattermost"
|
||||||
@@ -2232,6 +2235,30 @@
|
|||||||
"id": "model.post.is_valid.user_id.app_error",
|
"id": "model.post.is_valid.user_id.app_error",
|
||||||
"translation": "Invalid user id"
|
"translation": "Invalid user id"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "model.compliance.is_valid.id.app_error",
|
||||||
|
"translation": "Invalid Id"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "model.compliance.is_valid.create_at.app_error",
|
||||||
|
"translation": "Create at must be a valid time"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "model.compliance.is_valid.desc.app_error",
|
||||||
|
"translation": "Invalid description"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "model.compliance.is_valid.start_at.app_error",
|
||||||
|
"translation": "From must be a valid time"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "model.compliance.is_valid.end_at.app_error",
|
||||||
|
"translation": "To must be a valid time"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "model.compliance.is_valid.start_end_at.app_error",
|
||||||
|
"translation": "To must be greater than From"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "model.preference.is_valid.category.app_error",
|
"id": "model.preference.is_valid.category.app_error",
|
||||||
"translation": "Invalid category"
|
"translation": "Invalid category"
|
||||||
@@ -2508,6 +2535,14 @@
|
|||||||
"id": "store.sql_audit.save.saving.app_error",
|
"id": "store.sql_audit.save.saving.app_error",
|
||||||
"translation": "We encountered an error saving the audit"
|
"translation": "We encountered an error saving the audit"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "store.sql_compliance.save.saving.app_error",
|
||||||
|
"translation": "We encountered an error saving the compliance report"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "store.sql_compliance.get.finding.app_error",
|
||||||
|
"translation": "We encountered an error retrieving the compliance reports"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "store.sql_channel.analytics_type_count.app_error",
|
"id": "store.sql_channel.analytics_type_count.app_error",
|
||||||
"translation": "We couldn't get channel type counts"
|
"translation": "We couldn't get channel type counts"
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ func main() {
|
|||||||
web.InitWeb()
|
web.InitWeb()
|
||||||
|
|
||||||
if model.BuildEnterpriseReady == "true" {
|
if model.BuildEnterpriseReady == "true" {
|
||||||
loadLicense()
|
api.LoadLicense()
|
||||||
}
|
}
|
||||||
|
|
||||||
if !utils.IsLicensed && len(utils.Cfg.SqlSettings.DataSourceReplicas) > 1 {
|
if !utils.IsLicensed && len(utils.Cfg.SqlSettings.DataSourceReplicas) > 1 {
|
||||||
@@ -106,26 +106,6 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadLicense() {
|
|
||||||
licenseId := ""
|
|
||||||
if result := <-api.Srv.Store.System().Get(); result.Err == nil {
|
|
||||||
props := result.Data.(model.StringMap)
|
|
||||||
licenseId = props[model.SYSTEM_ACTIVE_LICENSE_ID]
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(licenseId) != 26 {
|
|
||||||
l4g.Warn(utils.T("mattermost.load_license.find.warn"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if result := <-api.Srv.Store.License().Get(licenseId); result.Err == nil {
|
|
||||||
record := result.Data.(*model.LicenseRecord)
|
|
||||||
utils.LoadLicense([]byte(record.Bytes))
|
|
||||||
} else {
|
|
||||||
l4g.Warn(utils.T("mattermost.load_license.find.warn"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func setDiagnosticId() {
|
func setDiagnosticId() {
|
||||||
if result := <-api.Srv.Store.System().Get(); result.Err == nil {
|
if result := <-api.Srv.Store.System().Get(); result.Err == nil {
|
||||||
props := result.Data.(model.StringMap)
|
props := result.Data.(model.StringMap)
|
||||||
|
|||||||
@@ -471,6 +471,42 @@ func (c *Client) TestEmail(config *Config) (*Result, *AppError) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) GetComplianceReports() (*Result, *AppError) {
|
||||||
|
if r, err := c.DoApiGet("/admin/compliance_reports", "", ""); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
|
r.Header.Get(HEADER_ETAG_SERVER), CompliancesFromJson(r.Body)}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) SaveComplianceReport(job *Compliance) (*Result, *AppError) {
|
||||||
|
if r, err := c.DoApiPost("/admin/save_compliance_report", job.ToJson()); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
|
r.Header.Get(HEADER_ETAG_SERVER), ComplianceFromJson(r.Body)}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) DownloadComplianceReport(id string) (*Result, *AppError) {
|
||||||
|
var rq *http.Request
|
||||||
|
rq, _ = http.NewRequest("GET", c.ApiUrl+"/admin/download_compliance_report/"+id, nil)
|
||||||
|
|
||||||
|
if len(c.AuthToken) > 0 {
|
||||||
|
rq.Header.Set(HEADER_AUTH, "BEARER "+c.AuthToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rp, err := c.HttpClient.Do(rq); err != nil {
|
||||||
|
return nil, NewLocAppError("/admin/download_compliance_report", "model.client.connecting.app_error", nil, err.Error())
|
||||||
|
} else if rp.StatusCode >= 300 {
|
||||||
|
return nil, AppErrorFromJson(rp.Body)
|
||||||
|
} else {
|
||||||
|
return &Result{rp.Header.Get(HEADER_REQUEST_ID),
|
||||||
|
rp.Header.Get(HEADER_ETAG_SERVER), rp.Body}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) GetTeamAnalytics(teamId, name string) (*Result, *AppError) {
|
func (c *Client) GetTeamAnalytics(teamId, name string) (*Result, *AppError) {
|
||||||
if r, err := c.DoApiGet("/admin/analytics/"+teamId+"/"+name, "", ""); err != nil {
|
if r, err := c.DoApiGet("/admin/analytics/"+teamId+"/"+name, "", ""); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
132
model/compliance.go
Обычный файл
132
model/compliance.go
Обычный файл
@@ -0,0 +1,132 @@
|
|||||||
|
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
COMPLIANCE_STATUS_CREATED = "created"
|
||||||
|
COMPLIANCE_STATUS_RUNNING = "running"
|
||||||
|
COMPLIANCE_STATUS_FINISHED = "finished"
|
||||||
|
COMPLIANCE_STATUS_FAILED = "failed"
|
||||||
|
COMPLIANCE_STATUS_REMOVED = "removed"
|
||||||
|
|
||||||
|
COMPLIANCE_TYPE_DAILY = "daily"
|
||||||
|
COMPLIANCE_TYPE_ADHOC = "adhoc"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Compliance struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
CreateAt int64 `json:"create_at"`
|
||||||
|
UserId string `json:"user_id"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
Count int `json:"count"`
|
||||||
|
Desc string `json:"desc"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
StartAt int64 `json:"start_at"`
|
||||||
|
EndAt int64 `json:"end_at"`
|
||||||
|
Keywords string `json:"keywords"`
|
||||||
|
Emails string `json:"emails"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Compliances []Compliance
|
||||||
|
|
||||||
|
func (o *Compliance) ToJson() string {
|
||||||
|
b, err := json.Marshal(o)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
} else {
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (me *Compliance) PreSave() {
|
||||||
|
if me.Id == "" {
|
||||||
|
me.Id = NewId()
|
||||||
|
}
|
||||||
|
|
||||||
|
if me.Status == "" {
|
||||||
|
me.Status = COMPLIANCE_STATUS_CREATED
|
||||||
|
}
|
||||||
|
|
||||||
|
me.Count = 0
|
||||||
|
me.Emails = strings.ToLower(me.Emails)
|
||||||
|
me.Keywords = strings.ToLower(me.Keywords)
|
||||||
|
|
||||||
|
me.CreateAt = GetMillis()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (me *Compliance) JobName() string {
|
||||||
|
jobName := me.Type
|
||||||
|
if me.Type == COMPLIANCE_TYPE_DAILY {
|
||||||
|
jobName += "-" + me.Desc
|
||||||
|
}
|
||||||
|
|
||||||
|
jobName += "-" + me.Id
|
||||||
|
|
||||||
|
return jobName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (me *Compliance) IsValid() *AppError {
|
||||||
|
|
||||||
|
if len(me.Id) != 26 {
|
||||||
|
return NewLocAppError("Compliance.IsValid", "model.compliance.is_valid.id.app_error", nil, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
if me.CreateAt == 0 {
|
||||||
|
return NewLocAppError("Compliance.IsValid", "model.compliance.is_valid.create_at.app_error", nil, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(me.Desc) > 512 || len(me.Desc) == 0 {
|
||||||
|
return NewLocAppError("Compliance.IsValid", "model.compliance.is_valid.desc.app_error", nil, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
if me.StartAt == 0 {
|
||||||
|
return NewLocAppError("Compliance.IsValid", "model.compliance.is_valid.start_at.app_error", nil, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
if me.EndAt == 0 {
|
||||||
|
return NewLocAppError("Compliance.IsValid", "model.compliance.is_valid.end_at.app_error", nil, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
if me.EndAt <= me.StartAt {
|
||||||
|
return NewLocAppError("Compliance.IsValid", "model.compliance.is_valid.start_end_at.app_error", nil, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ComplianceFromJson(data io.Reader) *Compliance {
|
||||||
|
decoder := json.NewDecoder(data)
|
||||||
|
var o Compliance
|
||||||
|
err := decoder.Decode(&o)
|
||||||
|
if err == nil {
|
||||||
|
return &o
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o Compliances) ToJson() string {
|
||||||
|
if b, err := json.Marshal(o); err != nil {
|
||||||
|
return "[]"
|
||||||
|
} else {
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func CompliancesFromJson(data io.Reader) Compliances {
|
||||||
|
decoder := json.NewDecoder(data)
|
||||||
|
var o Compliances
|
||||||
|
err := decoder.Decode(&o)
|
||||||
|
if err == nil {
|
||||||
|
return o
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -65,6 +65,17 @@ func CompliancePostHeader() []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (me *CompliancePost) Row() []string {
|
func (me *CompliancePost) Row() []string {
|
||||||
|
|
||||||
|
postDeleteAt := ""
|
||||||
|
if me.PostDeleteAt > 0 {
|
||||||
|
postDeleteAt = time.Unix(0, me.PostDeleteAt*int64(1000*1000)).Format(time.RFC3339)
|
||||||
|
}
|
||||||
|
|
||||||
|
postUpdateAt := ""
|
||||||
|
if me.PostUpdateAt != me.PostCreateAt {
|
||||||
|
postUpdateAt = time.Unix(0, me.PostUpdateAt*int64(1000*1000)).Format(time.RFC3339)
|
||||||
|
}
|
||||||
|
|
||||||
return []string{
|
return []string{
|
||||||
me.TeamName,
|
me.TeamName,
|
||||||
me.TeamDisplayName,
|
me.TeamDisplayName,
|
||||||
@@ -77,9 +88,10 @@ func (me *CompliancePost) Row() []string {
|
|||||||
me.UserNickname,
|
me.UserNickname,
|
||||||
|
|
||||||
me.PostId,
|
me.PostId,
|
||||||
time.Unix(0, me.PostCreateAt*1000).Format(time.RFC3339),
|
time.Unix(0, me.PostCreateAt*int64(1000*1000)).Format(time.RFC3339),
|
||||||
time.Unix(0, me.PostUpdateAt*1000).Format(time.RFC3339),
|
postUpdateAt,
|
||||||
time.Unix(0, me.PostDeleteAt*1000).Format(time.RFC3339),
|
postDeleteAt,
|
||||||
|
|
||||||
me.PostRootId,
|
me.PostRootId,
|
||||||
me.PostParentId,
|
me.PostParentId,
|
||||||
me.PostOriginalId,
|
me.PostOriginalId,
|
||||||
|
|||||||
19
model/compliance_test.go
Обычный файл
19
model/compliance_test.go
Обычный файл
@@ -0,0 +1,19 @@
|
|||||||
|
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCompliance(t *testing.T) {
|
||||||
|
o := Compliance{Desc: "test", CreateAt: GetMillis()}
|
||||||
|
json := o.ToJson()
|
||||||
|
result := ComplianceFromJson(strings.NewReader(json))
|
||||||
|
|
||||||
|
if o.Desc != result.Desc {
|
||||||
|
t.Fatal("JobName do not match")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -170,19 +170,26 @@ type LdapSettings struct {
|
|||||||
QueryTimeout *int
|
QueryTimeout *int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ComplianceSettings struct {
|
||||||
|
Enable *bool
|
||||||
|
Directory *string
|
||||||
|
EnableDaily *bool
|
||||||
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
ServiceSettings ServiceSettings
|
ServiceSettings ServiceSettings
|
||||||
TeamSettings TeamSettings
|
TeamSettings TeamSettings
|
||||||
SqlSettings SqlSettings
|
SqlSettings SqlSettings
|
||||||
LogSettings LogSettings
|
LogSettings LogSettings
|
||||||
FileSettings FileSettings
|
FileSettings FileSettings
|
||||||
EmailSettings EmailSettings
|
EmailSettings EmailSettings
|
||||||
RateLimitSettings RateLimitSettings
|
RateLimitSettings RateLimitSettings
|
||||||
PrivacySettings PrivacySettings
|
PrivacySettings PrivacySettings
|
||||||
SupportSettings SupportSettings
|
SupportSettings SupportSettings
|
||||||
GitLabSettings SSOSettings
|
GitLabSettings SSOSettings
|
||||||
GoogleSettings SSOSettings
|
GoogleSettings SSOSettings
|
||||||
LdapSettings LdapSettings
|
LdapSettings LdapSettings
|
||||||
|
ComplianceSettings ComplianceSettings
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Config) ToJson() string {
|
func (o *Config) ToJson() string {
|
||||||
@@ -383,6 +390,21 @@ func (o *Config) SetDefaults() {
|
|||||||
o.ServiceSettings.AllowCorsFrom = new(string)
|
o.ServiceSettings.AllowCorsFrom = new(string)
|
||||||
*o.ServiceSettings.AllowCorsFrom = ""
|
*o.ServiceSettings.AllowCorsFrom = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if o.ComplianceSettings.Enable == nil {
|
||||||
|
o.ComplianceSettings.Enable = new(bool)
|
||||||
|
*o.ComplianceSettings.Enable = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if o.ComplianceSettings.Directory == nil {
|
||||||
|
o.ComplianceSettings.Directory = new(string)
|
||||||
|
*o.ComplianceSettings.Directory = "./data/"
|
||||||
|
}
|
||||||
|
|
||||||
|
if o.ComplianceSettings.EnableDaily == nil {
|
||||||
|
o.ComplianceSettings.EnableDaily = new(bool)
|
||||||
|
*o.ComplianceSettings.EnableDaily = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Config) IsValid() *AppError {
|
func (o *Config) IsValid() *AppError {
|
||||||
|
|||||||
@@ -32,9 +32,10 @@ type Customer struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Features struct {
|
type Features struct {
|
||||||
Users *int `json:"users"`
|
Users *int `json:"users"`
|
||||||
LDAP *bool `json:"ldap"`
|
LDAP *bool `json:"ldap"`
|
||||||
GoogleSSO *bool `json:"google_sso"`
|
GoogleSSO *bool `json:"google_sso"`
|
||||||
|
Compliance *bool `json:"compliance"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Features) SetDefaults() {
|
func (f *Features) SetDefaults() {
|
||||||
@@ -52,6 +53,11 @@ func (f *Features) SetDefaults() {
|
|||||||
f.GoogleSSO = new(bool)
|
f.GoogleSSO = new(bool)
|
||||||
*f.GoogleSSO = true
|
*f.GoogleSSO = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if f.Compliance == nil {
|
||||||
|
f.Compliance = new(bool)
|
||||||
|
*f.Compliance = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *License) IsExpired() bool {
|
func (l *License) IsExpired() bool {
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ func NewSqlAuditStore(sqlStore *SqlStore) AuditStore {
|
|||||||
table := db.AddTableWithName(model.Audit{}, "Audits").SetKeys(false, "Id")
|
table := db.AddTableWithName(model.Audit{}, "Audits").SetKeys(false, "Id")
|
||||||
table.ColMap("Id").SetMaxSize(26)
|
table.ColMap("Id").SetMaxSize(26)
|
||||||
table.ColMap("UserId").SetMaxSize(26)
|
table.ColMap("UserId").SetMaxSize(26)
|
||||||
table.ColMap("Action").SetMaxSize(64)
|
table.ColMap("Action").SetMaxSize(512)
|
||||||
table.ColMap("ExtraInfo").SetMaxSize(128)
|
table.ColMap("ExtraInfo").SetMaxSize(1024)
|
||||||
table.ColMap("IpAddress").SetMaxSize(64)
|
table.ColMap("IpAddress").SetMaxSize(64)
|
||||||
table.ColMap("SessionId").SetMaxSize(26)
|
table.ColMap("SessionId").SetMaxSize(26)
|
||||||
}
|
}
|
||||||
@@ -28,6 +28,17 @@ func NewSqlAuditStore(sqlStore *SqlStore) AuditStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s SqlAuditStore) UpgradeSchemaIfNeeded() {
|
func (s SqlAuditStore) UpgradeSchemaIfNeeded() {
|
||||||
|
// ADDED for 2.2 REMOVE for 2.6
|
||||||
|
extraLength := s.GetMaxLengthOfColumnIfExists("Audits", "ExtraInfo")
|
||||||
|
if len(extraLength) > 0 && extraLength != "1024" {
|
||||||
|
s.AlterColumnTypeIfExists("Audits", "ExtraInfo", "VARCHAR(1024)", "VARCHAR(1024)")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ADDED for 2.2 REMOVE for 2.6
|
||||||
|
actionLength := s.GetMaxLengthOfColumnIfExists("Audits", "Action")
|
||||||
|
if len(actionLength) > 0 && actionLength != "512" {
|
||||||
|
s.AlterColumnTypeIfExists("Audits", "Action", "VARCHAR(512)", "VARCHAR(512)")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s SqlAuditStore) CreateIndexesIfNotExists() {
|
func (s SqlAuditStore) CreateIndexesIfNotExists() {
|
||||||
|
|||||||
234
store/sql_compliance_store.go
Обычный файл
234
store/sql_compliance_store.go
Обычный файл
@@ -0,0 +1,234 @@
|
|||||||
|
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
package store
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/mattermost/platform/model"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SqlComplianceStore struct {
|
||||||
|
*SqlStore
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSqlComplianceStore(sqlStore *SqlStore) ComplianceStore {
|
||||||
|
s := &SqlComplianceStore{sqlStore}
|
||||||
|
|
||||||
|
for _, db := range sqlStore.GetAllConns() {
|
||||||
|
table := db.AddTableWithName(model.Compliance{}, "Compliances").SetKeys(false, "Id")
|
||||||
|
table.ColMap("Id").SetMaxSize(26)
|
||||||
|
table.ColMap("UserId").SetMaxSize(26)
|
||||||
|
table.ColMap("Status").SetMaxSize(64)
|
||||||
|
table.ColMap("Desc").SetMaxSize(512)
|
||||||
|
table.ColMap("Type").SetMaxSize(64)
|
||||||
|
table.ColMap("Keywords").SetMaxSize(512)
|
||||||
|
table.ColMap("Emails").SetMaxSize(1024)
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s SqlComplianceStore) UpgradeSchemaIfNeeded() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s SqlComplianceStore) CreateIndexesIfNotExists() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s SqlComplianceStore) Save(compliance *model.Compliance) StoreChannel {
|
||||||
|
|
||||||
|
storeChannel := make(StoreChannel)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
result := StoreResult{}
|
||||||
|
|
||||||
|
compliance.PreSave()
|
||||||
|
if result.Err = compliance.IsValid(); result.Err != nil {
|
||||||
|
storeChannel <- result
|
||||||
|
close(storeChannel)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.GetMaster().Insert(compliance); err != nil {
|
||||||
|
result.Err = model.NewLocAppError("SqlComplianceStore.Save", "store.sql_compliance.save.saving.app_error", nil, err.Error())
|
||||||
|
} else {
|
||||||
|
result.Data = compliance
|
||||||
|
}
|
||||||
|
|
||||||
|
storeChannel <- result
|
||||||
|
close(storeChannel)
|
||||||
|
}()
|
||||||
|
|
||||||
|
return storeChannel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (us SqlComplianceStore) Update(compliance *model.Compliance) StoreChannel {
|
||||||
|
|
||||||
|
storeChannel := make(StoreChannel)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
result := StoreResult{}
|
||||||
|
|
||||||
|
if result.Err = compliance.IsValid(); result.Err != nil {
|
||||||
|
storeChannel <- result
|
||||||
|
close(storeChannel)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := us.GetMaster().Update(compliance); err != nil {
|
||||||
|
result.Err = model.NewLocAppError("SqlComplianceStore.Update", "store.sql_compliance.save.saving.app_error", nil, err.Error())
|
||||||
|
} else {
|
||||||
|
result.Data = compliance
|
||||||
|
}
|
||||||
|
|
||||||
|
storeChannel <- result
|
||||||
|
close(storeChannel)
|
||||||
|
}()
|
||||||
|
|
||||||
|
return storeChannel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s SqlComplianceStore) GetAll() StoreChannel {
|
||||||
|
|
||||||
|
storeChannel := make(StoreChannel)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
result := StoreResult{}
|
||||||
|
|
||||||
|
query := "SELECT * FROM Compliances ORDER BY CreateAt DESC"
|
||||||
|
|
||||||
|
var compliances model.Compliances
|
||||||
|
if _, err := s.GetReplica().Select(&compliances, query); err != nil {
|
||||||
|
result.Err = model.NewLocAppError("SqlComplianceStore.Get", "store.sql_compliance.get.finding.app_error", nil, err.Error())
|
||||||
|
} else {
|
||||||
|
result.Data = compliances
|
||||||
|
}
|
||||||
|
|
||||||
|
storeChannel <- result
|
||||||
|
close(storeChannel)
|
||||||
|
}()
|
||||||
|
|
||||||
|
return storeChannel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (us SqlComplianceStore) Get(id string) StoreChannel {
|
||||||
|
|
||||||
|
storeChannel := make(StoreChannel)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
result := StoreResult{}
|
||||||
|
|
||||||
|
if obj, err := us.GetReplica().Get(model.Compliance{}, id); err != nil {
|
||||||
|
result.Err = model.NewLocAppError("SqlComplianceStore.Get", "store.sql_compliance.get.finding.app_error", nil, err.Error())
|
||||||
|
} else if obj == nil {
|
||||||
|
result.Err = model.NewLocAppError("SqlComplianceStore.Get", "store.sql_compliance.get.finding.app_error", nil, err.Error())
|
||||||
|
} else {
|
||||||
|
result.Data = obj.(*model.Compliance)
|
||||||
|
}
|
||||||
|
|
||||||
|
storeChannel <- result
|
||||||
|
close(storeChannel)
|
||||||
|
|
||||||
|
}()
|
||||||
|
|
||||||
|
return storeChannel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s SqlComplianceStore) ComplianceExport(job *model.Compliance) StoreChannel {
|
||||||
|
storeChannel := make(StoreChannel)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
result := StoreResult{}
|
||||||
|
|
||||||
|
props := map[string]interface{}{"StartTime": job.StartAt, "EndTime": job.EndAt}
|
||||||
|
|
||||||
|
keywordQuery := ""
|
||||||
|
keywords := strings.Fields(strings.TrimSpace(strings.ToLower(strings.Replace(job.Keywords, ",", " ", -1))))
|
||||||
|
if len(keywords) > 0 {
|
||||||
|
|
||||||
|
keywordQuery = "AND ("
|
||||||
|
|
||||||
|
for index, keyword := range keywords {
|
||||||
|
if index >= 1 {
|
||||||
|
keywordQuery += " OR LOWER(Posts.Message) LIKE :Keyword" + strconv.Itoa(index)
|
||||||
|
} else {
|
||||||
|
keywordQuery += "LOWER(Posts.Message) LIKE :Keyword" + strconv.Itoa(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
props["Keyword"+strconv.Itoa(index)] = "%" + keyword + "%"
|
||||||
|
}
|
||||||
|
|
||||||
|
keywordQuery += ")"
|
||||||
|
}
|
||||||
|
|
||||||
|
emailQuery := ""
|
||||||
|
emails := strings.Fields(strings.TrimSpace(strings.ToLower(strings.Replace(job.Emails, ",", " ", -1))))
|
||||||
|
if len(emails) > 0 {
|
||||||
|
|
||||||
|
emailQuery = "AND ("
|
||||||
|
|
||||||
|
for index, email := range emails {
|
||||||
|
if index >= 1 {
|
||||||
|
emailQuery += " OR Users.Email = :Email" + strconv.Itoa(index)
|
||||||
|
} else {
|
||||||
|
emailQuery += "Users.Email = :Email" + strconv.Itoa(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
props["Email"+strconv.Itoa(index)] = email
|
||||||
|
}
|
||||||
|
|
||||||
|
emailQuery += ")"
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
` + emailQuery + `
|
||||||
|
` + keywordQuery + `
|
||||||
|
ORDER BY Posts.CreateAt
|
||||||
|
LIMIT 30000`
|
||||||
|
|
||||||
|
var cposts []*model.CompliancePost
|
||||||
|
|
||||||
|
if _, err := s.GetReplica().Select(&cposts, query, props); 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
|
||||||
|
}
|
||||||
210
store/sql_compliance_store_test.go
Обычный файл
210
store/sql_compliance_store_test.go
Обычный файл
@@ -0,0 +1,210 @@
|
|||||||
|
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
package store
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/mattermost/platform/model"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSqlComplianceStore(t *testing.T) {
|
||||||
|
Setup()
|
||||||
|
|
||||||
|
compliance1 := &model.Compliance{Desc: "Desc", UserId: model.NewId(), Status: "TestStatus1", StartAt: model.GetMillis() - 1, EndAt: model.GetMillis() + 1}
|
||||||
|
Must(store.Compliance().Save(compliance1))
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
||||||
|
compliance2 := &model.Compliance{Desc: "Desc", UserId: model.NewId(), Status: "TestStatus2", StartAt: model.GetMillis() - 1, EndAt: model.GetMillis() + 1}
|
||||||
|
Must(store.Compliance().Save(compliance2))
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
||||||
|
c := store.Compliance().GetAll()
|
||||||
|
result := <-c
|
||||||
|
compliances := result.Data.(model.Compliances)
|
||||||
|
|
||||||
|
if compliances[0].Status != "TestStatus2" && compliance2.Id != compliances[0].Id {
|
||||||
|
t.Fatal()
|
||||||
|
}
|
||||||
|
|
||||||
|
compliance2.Status = "TestUpdateStatus2"
|
||||||
|
Must(store.Compliance().Update(compliance2))
|
||||||
|
|
||||||
|
c = store.Compliance().GetAll()
|
||||||
|
result = <-c
|
||||||
|
compliances = result.Data.(model.Compliances)
|
||||||
|
|
||||||
|
if compliances[0].Status != "TestUpdateStatus2" && compliance2.Id != compliances[0].Id {
|
||||||
|
t.Fatal()
|
||||||
|
}
|
||||||
|
|
||||||
|
rc2 := (<-store.Compliance().Get(compliance2.Id)).Data.(*model.Compliance)
|
||||||
|
if rc2.Status != compliance2.Status {
|
||||||
|
t.Fatal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
u2 := &model.User{}
|
||||||
|
u2.TeamId = t1.Id
|
||||||
|
u2.Email = model.NewId()
|
||||||
|
u2.Username = model.NewId()
|
||||||
|
u2 = Must(store.User().Save(u2)).(*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 = u2.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)
|
||||||
|
|
||||||
|
cr1 := &model.Compliance{Desc: "test" + model.NewId(), StartAt: o1.CreateAt - 1, EndAt: o2a.CreateAt + 1}
|
||||||
|
if r1 := <-store.Compliance().ComplianceExport(cr1); r1.Err != nil {
|
||||||
|
t.Fatal(r1.Err)
|
||||||
|
} else {
|
||||||
|
cposts := r1.Data.([]*model.CompliancePost)
|
||||||
|
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cr2 := &model.Compliance{Desc: "test" + model.NewId(), StartAt: o1.CreateAt - 1, EndAt: o2a.CreateAt + 1, Emails: u2.Email}
|
||||||
|
if r1 := <-store.Compliance().ComplianceExport(cr2); r1.Err != nil {
|
||||||
|
t.Fatal(r1.Err)
|
||||||
|
} else {
|
||||||
|
cposts := r1.Data.([]*model.CompliancePost)
|
||||||
|
|
||||||
|
if len(cposts) != 1 {
|
||||||
|
t.Fatal("return wrong results length")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cposts[0].PostId != o2a.Id {
|
||||||
|
t.Fatal("Wrong sort")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cr3 := &model.Compliance{Desc: "test" + model.NewId(), StartAt: o1.CreateAt - 1, EndAt: o2a.CreateAt + 1, Emails: u2.Email + ", " + u1.Email}
|
||||||
|
if r1 := <-store.Compliance().ComplianceExport(cr3); r1.Err != nil {
|
||||||
|
t.Fatal(r1.Err)
|
||||||
|
} else {
|
||||||
|
cposts := r1.Data.([]*model.CompliancePost)
|
||||||
|
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cr4 := &model.Compliance{Desc: "test" + model.NewId(), StartAt: o1.CreateAt - 1, EndAt: o2a.CreateAt + 1, Keywords: o2a.Message}
|
||||||
|
if r1 := <-store.Compliance().ComplianceExport(cr4); r1.Err != nil {
|
||||||
|
t.Fatal(r1.Err)
|
||||||
|
} else {
|
||||||
|
cposts := r1.Data.([]*model.CompliancePost)
|
||||||
|
|
||||||
|
if len(cposts) != 1 {
|
||||||
|
t.Fatal("return wrong results length")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cposts[0].PostId != o2a.Id {
|
||||||
|
t.Fatal("Wrong sort")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cr5 := &model.Compliance{Desc: "test" + model.NewId(), StartAt: o1.CreateAt - 1, EndAt: o2a.CreateAt + 1, Keywords: o2a.Message + " " + o1.Message}
|
||||||
|
if r1 := <-store.Compliance().ComplianceExport(cr5); r1.Err != nil {
|
||||||
|
t.Fatal(r1.Err)
|
||||||
|
} else {
|
||||||
|
cposts := r1.Data.([]*model.CompliancePost)
|
||||||
|
|
||||||
|
if len(cposts) != 2 {
|
||||||
|
t.Fatal("return wrong results length")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cposts[0].PostId != o1.Id {
|
||||||
|
t.Fatal("Wrong sort")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cr6 := &model.Compliance{Desc: "test" + model.NewId(), StartAt: o1.CreateAt - 1, EndAt: o2a.CreateAt + 1, Emails: u2.Email + ", " + u1.Email, Keywords: o2a.Message + " " + o1.Message}
|
||||||
|
if r1 := <-store.Compliance().ComplianceExport(cr6); r1.Err != nil {
|
||||||
|
t.Fatal(r1.Err)
|
||||||
|
} else {
|
||||||
|
cposts := r1.Data.([]*model.CompliancePost)
|
||||||
|
|
||||||
|
if len(cposts) != 2 {
|
||||||
|
t.Fatal("return wrong results length")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cposts[0].PostId != o1.Id {
|
||||||
|
t.Fatal("Wrong sort")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cposts[1].PostId != o2a.Id {
|
||||||
|
t.Fatal("Wrong sort")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -979,59 +979,3 @@ func (s SqlPostStore) AnalyticsPostCount(teamId string, mustHaveFile bool, mustH
|
|||||||
|
|
||||||
return storeChannel
|
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
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -895,79 +895,3 @@ 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")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ type SqlStore struct {
|
|||||||
post PostStore
|
post PostStore
|
||||||
user UserStore
|
user UserStore
|
||||||
audit AuditStore
|
audit AuditStore
|
||||||
|
compliance ComplianceStore
|
||||||
session SessionStore
|
session SessionStore
|
||||||
oauth OAuthStore
|
oauth OAuthStore
|
||||||
system SystemStore
|
system SystemStore
|
||||||
@@ -98,6 +99,7 @@ func NewSqlStore() Store {
|
|||||||
sqlStore.post = NewSqlPostStore(sqlStore)
|
sqlStore.post = NewSqlPostStore(sqlStore)
|
||||||
sqlStore.user = NewSqlUserStore(sqlStore)
|
sqlStore.user = NewSqlUserStore(sqlStore)
|
||||||
sqlStore.audit = NewSqlAuditStore(sqlStore)
|
sqlStore.audit = NewSqlAuditStore(sqlStore)
|
||||||
|
sqlStore.compliance = NewSqlComplianceStore(sqlStore)
|
||||||
sqlStore.session = NewSqlSessionStore(sqlStore)
|
sqlStore.session = NewSqlSessionStore(sqlStore)
|
||||||
sqlStore.oauth = NewSqlOAuthStore(sqlStore)
|
sqlStore.oauth = NewSqlOAuthStore(sqlStore)
|
||||||
sqlStore.system = NewSqlSystemStore(sqlStore)
|
sqlStore.system = NewSqlSystemStore(sqlStore)
|
||||||
@@ -116,6 +118,7 @@ func NewSqlStore() Store {
|
|||||||
sqlStore.post.(*SqlPostStore).UpgradeSchemaIfNeeded()
|
sqlStore.post.(*SqlPostStore).UpgradeSchemaIfNeeded()
|
||||||
sqlStore.user.(*SqlUserStore).UpgradeSchemaIfNeeded()
|
sqlStore.user.(*SqlUserStore).UpgradeSchemaIfNeeded()
|
||||||
sqlStore.audit.(*SqlAuditStore).UpgradeSchemaIfNeeded()
|
sqlStore.audit.(*SqlAuditStore).UpgradeSchemaIfNeeded()
|
||||||
|
sqlStore.compliance.(*SqlComplianceStore).UpgradeSchemaIfNeeded()
|
||||||
sqlStore.session.(*SqlSessionStore).UpgradeSchemaIfNeeded()
|
sqlStore.session.(*SqlSessionStore).UpgradeSchemaIfNeeded()
|
||||||
sqlStore.oauth.(*SqlOAuthStore).UpgradeSchemaIfNeeded()
|
sqlStore.oauth.(*SqlOAuthStore).UpgradeSchemaIfNeeded()
|
||||||
sqlStore.system.(*SqlSystemStore).UpgradeSchemaIfNeeded()
|
sqlStore.system.(*SqlSystemStore).UpgradeSchemaIfNeeded()
|
||||||
@@ -129,6 +132,7 @@ func NewSqlStore() Store {
|
|||||||
sqlStore.post.(*SqlPostStore).CreateIndexesIfNotExists()
|
sqlStore.post.(*SqlPostStore).CreateIndexesIfNotExists()
|
||||||
sqlStore.user.(*SqlUserStore).CreateIndexesIfNotExists()
|
sqlStore.user.(*SqlUserStore).CreateIndexesIfNotExists()
|
||||||
sqlStore.audit.(*SqlAuditStore).CreateIndexesIfNotExists()
|
sqlStore.audit.(*SqlAuditStore).CreateIndexesIfNotExists()
|
||||||
|
sqlStore.compliance.(*SqlComplianceStore).CreateIndexesIfNotExists()
|
||||||
sqlStore.session.(*SqlSessionStore).CreateIndexesIfNotExists()
|
sqlStore.session.(*SqlSessionStore).CreateIndexesIfNotExists()
|
||||||
sqlStore.oauth.(*SqlOAuthStore).CreateIndexesIfNotExists()
|
sqlStore.oauth.(*SqlOAuthStore).CreateIndexesIfNotExists()
|
||||||
sqlStore.system.(*SqlSystemStore).CreateIndexesIfNotExists()
|
sqlStore.system.(*SqlSystemStore).CreateIndexesIfNotExists()
|
||||||
@@ -591,6 +595,10 @@ func (ss SqlStore) Audit() AuditStore {
|
|||||||
return ss.audit
|
return ss.audit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ss SqlStore) Compliance() ComplianceStore {
|
||||||
|
return ss.compliance
|
||||||
|
}
|
||||||
|
|
||||||
func (ss SqlStore) OAuth() OAuthStore {
|
func (ss SqlStore) OAuth() OAuthStore {
|
||||||
return ss.oauth
|
return ss.oauth
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ type Store interface {
|
|||||||
Post() PostStore
|
Post() PostStore
|
||||||
User() UserStore
|
User() UserStore
|
||||||
Audit() AuditStore
|
Audit() AuditStore
|
||||||
|
Compliance() ComplianceStore
|
||||||
Session() SessionStore
|
Session() SessionStore
|
||||||
OAuth() OAuthStore
|
OAuth() OAuthStore
|
||||||
System() SystemStore
|
System() SystemStore
|
||||||
@@ -105,7 +106,6 @@ type PostStore interface {
|
|||||||
AnalyticsUserCountsWithPostsByDay(teamId string) StoreChannel
|
AnalyticsUserCountsWithPostsByDay(teamId string) StoreChannel
|
||||||
AnalyticsPostCountsByDay(teamId string) StoreChannel
|
AnalyticsPostCountsByDay(teamId string) StoreChannel
|
||||||
AnalyticsPostCount(teamId string, mustHaveFile bool, mustHaveHashtag bool) StoreChannel
|
AnalyticsPostCount(teamId string, mustHaveFile bool, mustHaveHashtag bool) StoreChannel
|
||||||
ComplianceExport(startTime int64, endTime int64) StoreChannel
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserStore interface {
|
type UserStore interface {
|
||||||
@@ -152,6 +152,14 @@ type AuditStore interface {
|
|||||||
PermanentDeleteByUser(userId string) StoreChannel
|
PermanentDeleteByUser(userId string) StoreChannel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ComplianceStore interface {
|
||||||
|
Save(compliance *model.Compliance) StoreChannel
|
||||||
|
Update(compliance *model.Compliance) StoreChannel
|
||||||
|
Get(id string) StoreChannel
|
||||||
|
GetAll() StoreChannel
|
||||||
|
ComplianceExport(compliance *model.Compliance) StoreChannel
|
||||||
|
}
|
||||||
|
|
||||||
type OAuthStore interface {
|
type OAuthStore interface {
|
||||||
SaveApp(app *model.OAuthApp) StoreChannel
|
SaveApp(app *model.OAuthApp) StoreChannel
|
||||||
UpdateApp(app *model.OAuthApp) StoreChannel
|
UpdateApp(app *model.OAuthApp) StoreChannel
|
||||||
|
|||||||
@@ -238,5 +238,7 @@ func getClientConfig(c *model.Config) map[string]string {
|
|||||||
|
|
||||||
props["AllowCorsFrom"] = *c.ServiceSettings.AllowCorsFrom
|
props["AllowCorsFrom"] = *c.ServiceSettings.AllowCorsFrom
|
||||||
|
|
||||||
|
props["EnableCompliance"] = strconv.FormatBool(*c.ComplianceSettings.Enable)
|
||||||
|
|
||||||
return props
|
return props
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,16 @@ type HTMLTemplate struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func InitHTML() {
|
func InitHTML() {
|
||||||
templatesDir := FindDir("templates")
|
InitHTMLWithDir("templates")
|
||||||
|
}
|
||||||
|
|
||||||
|
func InitHTMLWithDir(dir string) {
|
||||||
|
|
||||||
|
if htmlTemplates != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
templatesDir := FindDir(dir)
|
||||||
l4g.Debug(T("api.api.init.parsing_templates.debug"), templatesDir)
|
l4g.Debug(T("api.api.init.parsing_templates.debug"), templatesDir)
|
||||||
var err error
|
var err error
|
||||||
if htmlTemplates, err = template.ParseGlob(templatesDir + "*.html"); err != nil {
|
if htmlTemplates, err = template.ParseGlob(templatesDir + "*.html"); err != nil {
|
||||||
|
|||||||
@@ -16,7 +16,11 @@ var T i18n.TranslateFunc
|
|||||||
var locales map[string]string = make(map[string]string)
|
var locales map[string]string = make(map[string]string)
|
||||||
|
|
||||||
func InitTranslations() {
|
func InitTranslations() {
|
||||||
i18nDirectory := FindDir("i18n")
|
InitTranslationsWithDir("i18n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func InitTranslationsWithDir(dir string) {
|
||||||
|
i18nDirectory := FindDir(dir)
|
||||||
files, _ := ioutil.ReadDir(i18nDirectory)
|
files, _ := ioutil.ReadDir(i18nDirectory)
|
||||||
for _, f := range files {
|
for _, f := range files {
|
||||||
if filepath.Ext(f.Name()) == ".json" {
|
if filepath.Ext(f.Name()) == ".json" {
|
||||||
|
|||||||
@@ -115,6 +115,7 @@ func getClientLicense(l *model.License) map[string]string {
|
|||||||
props["Users"] = strconv.Itoa(*l.Features.Users)
|
props["Users"] = strconv.Itoa(*l.Features.Users)
|
||||||
props["LDAP"] = strconv.FormatBool(*l.Features.LDAP)
|
props["LDAP"] = strconv.FormatBool(*l.Features.LDAP)
|
||||||
props["GoogleSSO"] = strconv.FormatBool(*l.Features.GoogleSSO)
|
props["GoogleSSO"] = strconv.FormatBool(*l.Features.GoogleSSO)
|
||||||
|
props["Compliance"] = strconv.FormatBool(*l.Features.Compliance)
|
||||||
props["IssuedAt"] = strconv.FormatInt(l.IssuedAt, 10)
|
props["IssuedAt"] = strconv.FormatInt(l.IssuedAt, 10)
|
||||||
props["StartsAt"] = strconv.FormatInt(l.StartsAt, 10)
|
props["StartsAt"] = strconv.FormatInt(l.StartsAt, 10)
|
||||||
props["ExpiresAt"] = strconv.FormatInt(l.ExpiresAt, 10)
|
props["ExpiresAt"] = strconv.FormatInt(l.ExpiresAt, 10)
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import LegalAndSupportSettingsTab from './legal_and_support_settings.jsx';
|
|||||||
import TeamUsersTab from './team_users.jsx';
|
import TeamUsersTab from './team_users.jsx';
|
||||||
import TeamAnalyticsTab from '../analytics/team_analytics.jsx';
|
import TeamAnalyticsTab from '../analytics/team_analytics.jsx';
|
||||||
import LdapSettingsTab from './ldap_settings.jsx';
|
import LdapSettingsTab from './ldap_settings.jsx';
|
||||||
|
import ComplianceSettingsTab from './compliance_settings.jsx';
|
||||||
import LicenseSettingsTab from './license_settings.jsx';
|
import LicenseSettingsTab from './license_settings.jsx';
|
||||||
import SystemAnalyticsTab from '../analytics/system_analytics.jsx';
|
import SystemAnalyticsTab from '../analytics/system_analytics.jsx';
|
||||||
|
|
||||||
@@ -156,6 +157,8 @@ export default class AdminController extends React.Component {
|
|||||||
tab = <LegalAndSupportSettingsTab config={this.state.config}/>;
|
tab = <LegalAndSupportSettingsTab config={this.state.config}/>;
|
||||||
} else if (this.state.selected === 'ldap_settings') {
|
} else if (this.state.selected === 'ldap_settings') {
|
||||||
tab = <LdapSettingsTab config={this.state.config}/>;
|
tab = <LdapSettingsTab config={this.state.config}/>;
|
||||||
|
} else if (this.state.selected === 'compliance_settings') {
|
||||||
|
tab = <ComplianceSettingsTab config={this.state.config}/>;
|
||||||
} else if (this.state.selected === 'license') {
|
} else if (this.state.selected === 'license') {
|
||||||
tab = <LicenseSettingsTab config={this.state.config}/>;
|
tab = <LicenseSettingsTab config={this.state.config}/>;
|
||||||
} else if (this.state.selected === 'team_users') {
|
} else if (this.state.selected === 'team_users') {
|
||||||
|
|||||||
@@ -176,6 +176,7 @@ export default class AdminSidebar extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let ldapSettings;
|
let ldapSettings;
|
||||||
|
let complianceSettings;
|
||||||
let licenseSettings;
|
let licenseSettings;
|
||||||
if (global.window.mm_config.BuildEnterpriseReady === 'true') {
|
if (global.window.mm_config.BuildEnterpriseReady === 'true') {
|
||||||
if (global.window.mm_license.IsLicensed === 'true') {
|
if (global.window.mm_license.IsLicensed === 'true') {
|
||||||
@@ -193,6 +194,21 @@ export default class AdminSidebar extends React.Component {
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
complianceSettings = (
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href='#'
|
||||||
|
className={this.isSelected('compliance_settings')}
|
||||||
|
onClick={this.handleClick.bind(this, 'compliance_settings', null)}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.sidebar.compliance'
|
||||||
|
defaultMessage='Compliance Settings'
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
licenseSettings = (
|
licenseSettings = (
|
||||||
@@ -386,6 +402,7 @@ export default class AdminSidebar extends React.Component {
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{ldapSettings}
|
{ldapSettings}
|
||||||
|
{complianceSettings}
|
||||||
<li>
|
<li>
|
||||||
<a
|
<a
|
||||||
href='#'
|
href='#'
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
import LoadingScreen from '../loading_screen.jsx';
|
import LoadingScreen from '../loading_screen.jsx';
|
||||||
import AuditTable from '../audit_table.jsx';
|
import AuditTable from '../audit_table.jsx';
|
||||||
|
import ComplianceReports from './compliance_reports.jsx';
|
||||||
|
|
||||||
import AdminStore from '../../stores/admin_store.jsx';
|
import AdminStore from '../../stores/admin_store.jsx';
|
||||||
|
|
||||||
@@ -58,36 +59,40 @@ export default class Audits extends React.Component {
|
|||||||
} else {
|
} else {
|
||||||
content = (
|
content = (
|
||||||
<div style={{margin: '10px'}}>
|
<div style={{margin: '10px'}}>
|
||||||
<AuditTable
|
<AuditTable
|
||||||
audits={this.state.audits}
|
audits={this.state.audits}
|
||||||
showUserId={true}
|
showUserId={true}
|
||||||
showIp={true}
|
showIp={true}
|
||||||
showSession={true}
|
showSession={true}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='panel'>
|
<div>
|
||||||
<h3>
|
<ComplianceReports/>
|
||||||
<FormattedMessage
|
|
||||||
id='admin.audits.title'
|
<div className='panel'>
|
||||||
defaultMessage='User Activity'
|
<h3>
|
||||||
/>
|
<FormattedMessage
|
||||||
</h3>
|
id='admin.audits.title'
|
||||||
<button
|
defaultMessage='User Activity'
|
||||||
type='submit'
|
/>
|
||||||
className='btn btn-primary'
|
</h3>
|
||||||
onClick={this.reload}
|
<button
|
||||||
>
|
type='submit'
|
||||||
<FormattedMessage
|
className='btn btn-primary'
|
||||||
id='admin.audits.reload'
|
onClick={this.reload}
|
||||||
defaultMessage='Reload'
|
>
|
||||||
/>
|
<FormattedMessage
|
||||||
</button>
|
id='admin.audits.reload'
|
||||||
<div className='log__panel'>
|
defaultMessage='Reload'
|
||||||
{content}
|
/>
|
||||||
|
</button>
|
||||||
|
<div className='audit__panel'>
|
||||||
|
{content}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
384
web/react/components/admin_console/compliance_reports.jsx
Обычный файл
384
web/react/components/admin_console/compliance_reports.jsx
Обычный файл
@@ -0,0 +1,384 @@
|
|||||||
|
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
import LoadingScreen from '../loading_screen.jsx';
|
||||||
|
import * as Utils from '../../utils/utils.jsx';
|
||||||
|
import AdminStore from '../../stores/admin_store.jsx';
|
||||||
|
import UserStore from '../../stores/user_store.jsx';
|
||||||
|
|
||||||
|
import * as Client from '../../utils/client.jsx';
|
||||||
|
import * as AsyncClient from '../../utils/async_client.jsx';
|
||||||
|
|
||||||
|
import {FormattedMessage, FormattedDate, FormattedTime} from 'mm-intl';
|
||||||
|
|
||||||
|
export default class ComplianceReports extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.onComplianceReportsListenerChange = this.onComplianceReportsListenerChange.bind(this);
|
||||||
|
this.reload = this.reload.bind(this);
|
||||||
|
this.runReport = this.runReport.bind(this);
|
||||||
|
this.getDateTime = this.getDateTime.bind(this);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
reports: AdminStore.getComplianceReports(),
|
||||||
|
serverError: null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
AdminStore.addComplianceReportsChangeListener(this.onComplianceReportsListenerChange);
|
||||||
|
|
||||||
|
if (global.window.mm_license.IsLicensed !== 'true' || global.window.mm_config.EnableCompliance !== 'true') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncClient.getComplianceReports();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
AdminStore.removeComplianceReportsChangeListener(this.onComplianceReportsListenerChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
onComplianceReportsListenerChange() {
|
||||||
|
this.setState({
|
||||||
|
reports: AdminStore.getComplianceReports()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
reload() {
|
||||||
|
AdminStore.saveComplianceReports(null);
|
||||||
|
this.setState({
|
||||||
|
reports: null,
|
||||||
|
serverError: null
|
||||||
|
});
|
||||||
|
|
||||||
|
AsyncClient.getComplianceReports();
|
||||||
|
}
|
||||||
|
|
||||||
|
runReport(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
$('#run-button').button('loading');
|
||||||
|
|
||||||
|
var job = {};
|
||||||
|
job.desc = ReactDOM.findDOMNode(this.refs.desc).value;
|
||||||
|
job.emails = ReactDOM.findDOMNode(this.refs.emails).value;
|
||||||
|
job.keywords = ReactDOM.findDOMNode(this.refs.keywords).value;
|
||||||
|
job.start_at = Date.parse(ReactDOM.findDOMNode(this.refs.from).value);
|
||||||
|
job.end_at = Date.parse(ReactDOM.findDOMNode(this.refs.to).value);
|
||||||
|
|
||||||
|
Client.saveComplianceReports(
|
||||||
|
job,
|
||||||
|
() => {
|
||||||
|
ReactDOM.findDOMNode(this.refs.emails).value = '';
|
||||||
|
ReactDOM.findDOMNode(this.refs.keywords).value = '';
|
||||||
|
ReactDOM.findDOMNode(this.refs.desc).value = '';
|
||||||
|
ReactDOM.findDOMNode(this.refs.from).value = '';
|
||||||
|
ReactDOM.findDOMNode(this.refs.to).value = '';
|
||||||
|
this.reload();
|
||||||
|
$('#run-button').button('reset');
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
this.setState({serverError: err.message});
|
||||||
|
$('#run-button').button('reset');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getDateTime(millis) {
|
||||||
|
const date = new Date(millis);
|
||||||
|
return (
|
||||||
|
<span style={{whiteSpace: 'nowrap'}}>
|
||||||
|
<FormattedDate
|
||||||
|
value={date}
|
||||||
|
day='2-digit'
|
||||||
|
month='short'
|
||||||
|
year='numeric'
|
||||||
|
/>
|
||||||
|
{' - '}
|
||||||
|
<FormattedTime
|
||||||
|
value={date}
|
||||||
|
hour='2-digit'
|
||||||
|
minute='2-digit'
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
var content = null;
|
||||||
|
|
||||||
|
if (global.window.mm_license.IsLicensed !== 'true' || global.window.mm_config.EnableCompliance !== 'true') {
|
||||||
|
return <div/>;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.state.reports === null) {
|
||||||
|
content = <LoadingScreen/>;
|
||||||
|
} else {
|
||||||
|
var list = [];
|
||||||
|
|
||||||
|
for (var i = 0; i < this.state.reports.length; i++) {
|
||||||
|
const report = this.state.reports[i];
|
||||||
|
|
||||||
|
var params = '';
|
||||||
|
if (report.type === 'adhoc') {
|
||||||
|
params = (
|
||||||
|
<span>
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.compliance_reports.from'
|
||||||
|
defaultMessage='From:'
|
||||||
|
/>{' '}{this.getDateTime(report.start_at)}
|
||||||
|
<br/>
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.compliance_reports.to'
|
||||||
|
defaultMessage='To:'
|
||||||
|
/>{' '}{this.getDateTime(report.end_at)}
|
||||||
|
<br/>
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.compliance_reports.emails'
|
||||||
|
defaultMessage='Emails:'
|
||||||
|
/>{' '}{report.emails}
|
||||||
|
<br/>
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.compliance_reports.keywords'
|
||||||
|
defaultMessage='Keywords:'
|
||||||
|
/>{' '}{report.keywords}
|
||||||
|
</span>);
|
||||||
|
}
|
||||||
|
|
||||||
|
var download = '';
|
||||||
|
if (report.status === 'finished') {
|
||||||
|
download = (
|
||||||
|
<a href={'/api/v1/admin/download_compliance_report/' + report.id}>
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.compliance_table.download'
|
||||||
|
defaultMessage='Download'
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
var status = report.status;
|
||||||
|
if (report.status === 'finished') {
|
||||||
|
status = (
|
||||||
|
<span style={{color: 'green'}}>{report.status}</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (report.status === 'failed') {
|
||||||
|
status = (
|
||||||
|
<span style={{color: 'red'}}>{report.status}</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
var user = report.user_id;
|
||||||
|
var profile = UserStore.getProfile(report.user_id);
|
||||||
|
if (profile) {
|
||||||
|
user = profile.email;
|
||||||
|
}
|
||||||
|
|
||||||
|
list[i] = (
|
||||||
|
<tr key={report.id}>
|
||||||
|
<td style={{whiteSpace: 'nowrap'}}>{download}</td>
|
||||||
|
<td>{this.getDateTime(report.create_at)}</td>
|
||||||
|
<td>{status}</td>
|
||||||
|
<td>{report.count}</td>
|
||||||
|
<td>{report.type}</td>
|
||||||
|
<td style={{whiteSpace: 'nowrap'}}>{report.desc}</td>
|
||||||
|
<td>{user}</td>
|
||||||
|
<td style={{whiteSpace: 'nowrap'}}>{params}</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
content = (
|
||||||
|
<div style={{margin: '10px'}}>
|
||||||
|
<table className='table'>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th></th>
|
||||||
|
<th>
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.compliance_table.timestamp'
|
||||||
|
defaultMessage='Timestamp'
|
||||||
|
/>
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.compliance_table.status'
|
||||||
|
defaultMessage='Status'
|
||||||
|
/>
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.compliance_table.records'
|
||||||
|
defaultMessage='Records'
|
||||||
|
/>
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.compliance_table.type'
|
||||||
|
defaultMessage='Type'
|
||||||
|
/>
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.compliance_table.desc'
|
||||||
|
defaultMessage='Description'
|
||||||
|
/>
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.compliance_table.userId'
|
||||||
|
defaultMessage='Requested By'
|
||||||
|
/>
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.compliance_table.params'
|
||||||
|
defaultMessage='Params'
|
||||||
|
/>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{list}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let serverError = '';
|
||||||
|
if (this.state.serverError) {
|
||||||
|
serverError = (
|
||||||
|
<div
|
||||||
|
className='form-group has-error'
|
||||||
|
style={{marginTop: '10px'}}
|
||||||
|
>
|
||||||
|
<label className='control-label'>{this.state.serverError}</label>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='panel'>
|
||||||
|
<h3>
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.compliance_reports.title'
|
||||||
|
defaultMessage='Compliance Reports'
|
||||||
|
/>
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td colSpan='5'
|
||||||
|
style={{paddingBottom: '6px'}}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.compliance_reports.desc'
|
||||||
|
defaultMessage='Job Name:'
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
style={{width: '425px'}}
|
||||||
|
type='text'
|
||||||
|
className='form-control'
|
||||||
|
id='desc'
|
||||||
|
ref='desc'
|
||||||
|
placeholder={Utils.localizeMessage('admin.compliance_reports.desc_placeholder', 'Ex "Audit 445 for HR"')}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.compliance_reports.from'
|
||||||
|
defaultMessage='From:'
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type='text'
|
||||||
|
className='form-control'
|
||||||
|
id='from'
|
||||||
|
ref='from'
|
||||||
|
placeholder={Utils.localizeMessage('admin.compliance_reports.from_placeholder', 'Ex "2016-03-11"')}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td style={{paddingLeft: '4px'}}>
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.compliance_reports.to'
|
||||||
|
defaultMessage='To:'
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type='text'
|
||||||
|
className='form-control'
|
||||||
|
id='to'
|
||||||
|
ref='to'
|
||||||
|
placeholder={Utils.localizeMessage('admin.compliance_reports.to_placeholder', 'Ex "2016-03-15"')}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td style={{paddingLeft: '4px'}}>
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.compliance_reports.emails'
|
||||||
|
defaultMessage='Emails:'
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
style={{width: '325px'}}
|
||||||
|
type='text'
|
||||||
|
className='form-control'
|
||||||
|
id='emails'
|
||||||
|
ref='emails'
|
||||||
|
placeholder={Utils.localizeMessage('admin.compliance_reports.emails_placeholder', 'Ex "bill@example.com, bob@example.com"')}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td style={{paddingLeft: '4px'}}>
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.compliance_reports.keywords'
|
||||||
|
defaultMessage='Keywords:'
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
style={{width: '250px'}}
|
||||||
|
type='text'
|
||||||
|
className='form-control'
|
||||||
|
id='keywords'
|
||||||
|
ref='keywords'
|
||||||
|
placeholder={Utils.localizeMessage('admin.compliance_reports.keywords_placeholder', 'Ex "shorting stock"')}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<button
|
||||||
|
id='run-button'
|
||||||
|
type='submit'
|
||||||
|
className='btn btn-primary'
|
||||||
|
onClick={this.runReport}
|
||||||
|
style={{marginTop: '20px', marginLeft: '20px'}}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.compliance_reports.run'
|
||||||
|
defaultMessage='Run'
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{serverError}
|
||||||
|
<div style={{marginTop: '20px'}}>
|
||||||
|
<button
|
||||||
|
type='submit'
|
||||||
|
className='btn btn-primary'
|
||||||
|
onClick={this.reload}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.compliance_reports.reload'
|
||||||
|
defaultMessage='Reload'
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className='compliance__panel'>
|
||||||
|
{content}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
271
web/react/components/admin_console/compliance_settings.jsx
Обычный файл
271
web/react/components/admin_console/compliance_settings.jsx
Обычный файл
@@ -0,0 +1,271 @@
|
|||||||
|
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
import * as Client from '../../utils/client.jsx';
|
||||||
|
import * as AsyncClient from '../../utils/async_client.jsx';
|
||||||
|
|
||||||
|
import {injectIntl, intlShape, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
|
||||||
|
|
||||||
|
var holders = defineMessages({
|
||||||
|
saving: {
|
||||||
|
id: 'admin.compliance.saving',
|
||||||
|
defaultMessage: 'Saving Config...'
|
||||||
|
},
|
||||||
|
directoryExample: {
|
||||||
|
id: 'admin.compliance.directoryExample',
|
||||||
|
defaultMessage: 'Ex "./data/"'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
class ComplianceSettings extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.handleSubmit = this.handleSubmit.bind(this);
|
||||||
|
this.handleChange = this.handleChange.bind(this);
|
||||||
|
this.handleEnable = this.handleEnable.bind(this);
|
||||||
|
this.handleDisable = this.handleDisable.bind(this);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
saveNeeded: false,
|
||||||
|
serverError: null,
|
||||||
|
enable: this.props.config.ComplianceSettings.Enable
|
||||||
|
};
|
||||||
|
}
|
||||||
|
handleChange() {
|
||||||
|
this.setState({saveNeeded: true});
|
||||||
|
}
|
||||||
|
handleEnable() {
|
||||||
|
this.setState({saveNeeded: true, enable: true});
|
||||||
|
}
|
||||||
|
handleDisable() {
|
||||||
|
this.setState({saveNeeded: true, enable: false});
|
||||||
|
}
|
||||||
|
handleSubmit(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
$('#save-button').button('loading');
|
||||||
|
|
||||||
|
const config = this.props.config;
|
||||||
|
config.ComplianceSettings.Enable = this.refs.Enable.checked;
|
||||||
|
config.ComplianceSettings.Directory = ReactDOM.findDOMNode(this.refs.Directory).value;
|
||||||
|
config.ComplianceSettings.EnableDaily = this.refs.EnableDaily.checked;
|
||||||
|
|
||||||
|
Client.saveConfig(
|
||||||
|
config,
|
||||||
|
() => {
|
||||||
|
AsyncClient.getConfig();
|
||||||
|
this.setState({
|
||||||
|
serverError: null,
|
||||||
|
saveNeeded: false
|
||||||
|
});
|
||||||
|
$('#save-button').button('reset');
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
this.setState({
|
||||||
|
serverError: err.message,
|
||||||
|
saveNeeded: true
|
||||||
|
});
|
||||||
|
$('#save-button').button('reset');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
const {formatMessage} = this.props.intl;
|
||||||
|
let serverError = '';
|
||||||
|
if (this.state.serverError) {
|
||||||
|
serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
let saveClass = 'btn';
|
||||||
|
if (this.state.saveNeeded) {
|
||||||
|
saveClass = 'btn btn-primary';
|
||||||
|
}
|
||||||
|
|
||||||
|
const licenseEnabled = global.window.mm_license.IsLicensed === 'true' && global.window.mm_license.Compliance === 'true';
|
||||||
|
|
||||||
|
let bannerContent;
|
||||||
|
if (!licenseEnabled) {
|
||||||
|
bannerContent = (
|
||||||
|
<div className='banner warning'>
|
||||||
|
<div className='banner__content'>
|
||||||
|
<FormattedHTMLMessage
|
||||||
|
id='admin.compliance.noLicense'
|
||||||
|
defaultMessage='<h4 class="banner__heading">Note:</h4><p>Compliance is an enterprise feature. Your current license does not support Compliance. Click <a href="http://mattermost.com"target="_blank">here</a> for information and pricing on enterprise licenses.</p>'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='wrapper--fixed'>
|
||||||
|
{bannerContent}
|
||||||
|
<h3>
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.compliance.title'
|
||||||
|
defaultMessage='Compliance Settings'
|
||||||
|
/>
|
||||||
|
</h3>
|
||||||
|
<form
|
||||||
|
className='form-horizontal'
|
||||||
|
role='form'
|
||||||
|
>
|
||||||
|
|
||||||
|
<div className='form-group'>
|
||||||
|
<label
|
||||||
|
className='control-label col-sm-4'
|
||||||
|
htmlFor='Enable'
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.compliance.enableTitle'
|
||||||
|
defaultMessage='Enable Compliance:'
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<div className='col-sm-8'>
|
||||||
|
<label className='radio-inline'>
|
||||||
|
<input
|
||||||
|
type='radio'
|
||||||
|
name='Enable'
|
||||||
|
value='true'
|
||||||
|
ref='Enable'
|
||||||
|
defaultChecked={this.props.config.ComplianceSettings.Enable}
|
||||||
|
onChange={this.handleEnable}
|
||||||
|
disabled={!licenseEnabled}
|
||||||
|
/>
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.compliance.true'
|
||||||
|
defaultMessage='true'
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label className='radio-inline'>
|
||||||
|
<input
|
||||||
|
type='radio'
|
||||||
|
name='Enable'
|
||||||
|
value='false'
|
||||||
|
defaultChecked={!this.props.config.ComplianceSettings.Enable}
|
||||||
|
onChange={this.handleDisable}
|
||||||
|
/>
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.compliance.false'
|
||||||
|
defaultMessage='false'
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<p className='help-text'>
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.compliance.enableDesc'
|
||||||
|
defaultMessage='When true, Mattermost allows compliance reporting'
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='form-group'>
|
||||||
|
<label
|
||||||
|
className='control-label col-sm-4'
|
||||||
|
htmlFor='Directory'
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.compliance.directoryTitle'
|
||||||
|
defaultMessage='Compliance Directory Location:'
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<div className='col-sm-8'>
|
||||||
|
<input
|
||||||
|
type='text'
|
||||||
|
className='form-control'
|
||||||
|
id='Directory'
|
||||||
|
ref='Directory'
|
||||||
|
placeholder={formatMessage(holders.directoryExample)}
|
||||||
|
defaultValue={this.props.config.ComplianceSettings.Directory}
|
||||||
|
onChange={this.handleChange}
|
||||||
|
disabled={!this.state.enable}
|
||||||
|
/>
|
||||||
|
<p className='help-text'>
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.compliance.directoryDescription'
|
||||||
|
defaultMessage='Directory to which compliance reports are written. If blank, will be set to ./data/.'
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='form-group'>
|
||||||
|
<label
|
||||||
|
className='control-label col-sm-4'
|
||||||
|
htmlFor='EnableDaily'
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.compliance.enableDailyTitle'
|
||||||
|
defaultMessage='Enable Daily Report:'
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<div className='col-sm-8'>
|
||||||
|
<label className='radio-inline'>
|
||||||
|
<input
|
||||||
|
type='radio'
|
||||||
|
name='EnableDaily'
|
||||||
|
value='true'
|
||||||
|
ref='EnableDaily'
|
||||||
|
defaultChecked={this.props.config.ComplianceSettings.EnableDaily}
|
||||||
|
onChange={this.handleChange}
|
||||||
|
disabled={!this.state.enable}
|
||||||
|
/>
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.compliance.true'
|
||||||
|
defaultMessage='true'
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label className='radio-inline'>
|
||||||
|
<input
|
||||||
|
type='radio'
|
||||||
|
name='EnableDaily'
|
||||||
|
value='false'
|
||||||
|
defaultChecked={!this.props.config.ComplianceSettings.EnableDaily}
|
||||||
|
disabled={!this.state.enable}
|
||||||
|
/>
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.compliance.false'
|
||||||
|
defaultMessage='false'
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<p className='help-text'>
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.compliance.enableDesc'
|
||||||
|
defaultMessage='When true, Mattermost will generate a daily compliance report.'
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='form-group'>
|
||||||
|
<div className='col-sm-12'>
|
||||||
|
{serverError}
|
||||||
|
<button
|
||||||
|
disabled={!this.state.saveNeeded}
|
||||||
|
type='submit'
|
||||||
|
className={saveClass}
|
||||||
|
onClick={this.handleSubmit}
|
||||||
|
id='save-button'
|
||||||
|
data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + formatMessage(holders.saving)}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.compliance.save'
|
||||||
|
defaultMessage='Save'
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ComplianceSettings.defaultProps = {
|
||||||
|
};
|
||||||
|
|
||||||
|
ComplianceSettings.propTypes = {
|
||||||
|
intl: intlShape.isRequired,
|
||||||
|
config: React.PropTypes.object
|
||||||
|
};
|
||||||
|
|
||||||
|
export default injectIntl(ComplianceSettings);
|
||||||
@@ -217,7 +217,12 @@ class AuditTable extends React.Component {
|
|||||||
|
|
||||||
let uContent;
|
let uContent;
|
||||||
if (this.props.showUserId) {
|
if (this.props.showUserId) {
|
||||||
uContent = <td>{auditInfo.userId}</td>;
|
var profile = UserStore.getProfile(auditInfo.userId);
|
||||||
|
if (profile) {
|
||||||
|
uContent = <td>{profile.email}</td>;
|
||||||
|
} else {
|
||||||
|
uContent = <td>{auditInfo.userId}</td>;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let iContent;
|
let iContent;
|
||||||
@@ -560,6 +565,8 @@ export function formatAuditInfo(audit, formatMessage) {
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
} else if (actionURL.indexOf('/admin/download_compliance_report') === 0) {
|
||||||
|
auditDesc = Utils.toTitleCase(audit.extra_info);
|
||||||
} else {
|
} else {
|
||||||
switch (actionURL) {
|
switch (actionURL) {
|
||||||
case '/logout':
|
case '/logout':
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ const LOG_CHANGE_EVENT = 'log_change';
|
|||||||
const SERVER_AUDIT_CHANGE_EVENT = 'server_audit_change';
|
const SERVER_AUDIT_CHANGE_EVENT = 'server_audit_change';
|
||||||
const CONFIG_CHANGE_EVENT = 'config_change';
|
const CONFIG_CHANGE_EVENT = 'config_change';
|
||||||
const ALL_TEAMS_EVENT = 'all_team_change';
|
const ALL_TEAMS_EVENT = 'all_team_change';
|
||||||
|
const SERVER_COMPLIANCE_REPORT_CHANGE_EVENT = 'server_compliance_reports_change';
|
||||||
|
|
||||||
class AdminStoreClass extends EventEmitter {
|
class AdminStoreClass extends EventEmitter {
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -22,6 +23,7 @@ class AdminStoreClass extends EventEmitter {
|
|||||||
this.audits = null;
|
this.audits = null;
|
||||||
this.config = null;
|
this.config = null;
|
||||||
this.teams = null;
|
this.teams = null;
|
||||||
|
this.complianceReports = null;
|
||||||
|
|
||||||
this.emitLogChange = this.emitLogChange.bind(this);
|
this.emitLogChange = this.emitLogChange.bind(this);
|
||||||
this.addLogChangeListener = this.addLogChangeListener.bind(this);
|
this.addLogChangeListener = this.addLogChangeListener.bind(this);
|
||||||
@@ -31,6 +33,10 @@ class AdminStoreClass extends EventEmitter {
|
|||||||
this.addAuditChangeListener = this.addAuditChangeListener.bind(this);
|
this.addAuditChangeListener = this.addAuditChangeListener.bind(this);
|
||||||
this.removeAuditChangeListener = this.removeAuditChangeListener.bind(this);
|
this.removeAuditChangeListener = this.removeAuditChangeListener.bind(this);
|
||||||
|
|
||||||
|
this.emitComplianceReportsChange = this.emitComplianceReportsChange.bind(this);
|
||||||
|
this.addComplianceReportsChangeListener = this.addComplianceReportsChangeListener.bind(this);
|
||||||
|
this.removeComplianceReportsChangeListener = this.removeComplianceReportsChangeListener.bind(this);
|
||||||
|
|
||||||
this.emitConfigChange = this.emitConfigChange.bind(this);
|
this.emitConfigChange = this.emitConfigChange.bind(this);
|
||||||
this.addConfigChangeListener = this.addConfigChangeListener.bind(this);
|
this.addConfigChangeListener = this.addConfigChangeListener.bind(this);
|
||||||
this.removeConfigChangeListener = this.removeConfigChangeListener.bind(this);
|
this.removeConfigChangeListener = this.removeConfigChangeListener.bind(this);
|
||||||
@@ -64,6 +70,18 @@ class AdminStoreClass extends EventEmitter {
|
|||||||
this.removeListener(SERVER_AUDIT_CHANGE_EVENT, callback);
|
this.removeListener(SERVER_AUDIT_CHANGE_EVENT, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emitComplianceReportsChange() {
|
||||||
|
this.emit(SERVER_COMPLIANCE_REPORT_CHANGE_EVENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
addComplianceReportsChangeListener(callback) {
|
||||||
|
this.on(SERVER_COMPLIANCE_REPORT_CHANGE_EVENT, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeComplianceReportsChangeListener(callback) {
|
||||||
|
this.removeListener(SERVER_COMPLIANCE_REPORT_CHANGE_EVENT, callback);
|
||||||
|
}
|
||||||
|
|
||||||
emitConfigChange() {
|
emitConfigChange() {
|
||||||
this.emit(CONFIG_CHANGE_EVENT);
|
this.emit(CONFIG_CHANGE_EVENT);
|
||||||
}
|
}
|
||||||
@@ -104,6 +122,14 @@ class AdminStoreClass extends EventEmitter {
|
|||||||
this.audits = audits;
|
this.audits = audits;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getComplianceReports() {
|
||||||
|
return this.complianceReports;
|
||||||
|
}
|
||||||
|
|
||||||
|
saveComplianceReports(complianceReports) {
|
||||||
|
this.complianceReports = complianceReports;
|
||||||
|
}
|
||||||
|
|
||||||
getConfig() {
|
getConfig() {
|
||||||
return this.config;
|
return this.config;
|
||||||
}
|
}
|
||||||
@@ -147,6 +173,10 @@ AdminStoreClass.dispatchToken = AppDispatcher.register((payload) => {
|
|||||||
AdminStore.saveAudits(action.audits);
|
AdminStore.saveAudits(action.audits);
|
||||||
AdminStore.emitAuditChange();
|
AdminStore.emitAuditChange();
|
||||||
break;
|
break;
|
||||||
|
case ActionTypes.RECEIVED_SERVER_COMPLIANCE_REPORTS:
|
||||||
|
AdminStore.saveComplianceReports(action.complianceReports);
|
||||||
|
AdminStore.emitComplianceReportsChange();
|
||||||
|
break;
|
||||||
case ActionTypes.RECEIVED_CONFIG:
|
case ActionTypes.RECEIVED_CONFIG:
|
||||||
AdminStore.saveConfig(action.config);
|
AdminStore.saveConfig(action.config);
|
||||||
AdminStore.emitConfigChange();
|
AdminStore.emitConfigChange();
|
||||||
|
|||||||
@@ -341,6 +341,32 @@ export function getServerAudits() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getComplianceReports() {
|
||||||
|
if (isCallInProgress('getComplianceReports')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
callTracker.getComplianceReports = utils.getTimestamp();
|
||||||
|
client.getComplianceReports(
|
||||||
|
(data, textStatus, xhr) => {
|
||||||
|
callTracker.getComplianceReports = 0;
|
||||||
|
|
||||||
|
if (xhr.status === 304 || !data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AppDispatcher.handleServerAction({
|
||||||
|
type: ActionTypes.RECEIVED_SERVER_COMPLIANCE_REPORTS,
|
||||||
|
complianceReports: data
|
||||||
|
});
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
callTracker.getComplianceReports = 0;
|
||||||
|
dispatchError(err, 'getComplianceReports');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function getConfig() {
|
export function getConfig() {
|
||||||
if (isCallInProgress('getConfig')) {
|
if (isCallInProgress('getConfig')) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -412,6 +412,35 @@ export function getAudits(userId, success, error) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getComplianceReports(success, error) {
|
||||||
|
$.ajax({
|
||||||
|
url: '/api/v1/admin/compliance_reports',
|
||||||
|
dataType: 'json',
|
||||||
|
contentType: 'application/json',
|
||||||
|
type: 'GET',
|
||||||
|
success,
|
||||||
|
error: function onError(xhr, status, err) {
|
||||||
|
var e = handleError('getComplianceReports', xhr, status, err);
|
||||||
|
error(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function saveComplianceReports(job, success, error) {
|
||||||
|
$.ajax({
|
||||||
|
url: '/api/v1/admin/save_compliance_report',
|
||||||
|
dataType: 'json',
|
||||||
|
contentType: 'application/json',
|
||||||
|
type: 'POST',
|
||||||
|
data: JSON.stringify(job),
|
||||||
|
success,
|
||||||
|
error: (xhr, status, err) => {
|
||||||
|
var e = handleError('saveComplianceReports', xhr, status, err);
|
||||||
|
error(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function getLogs(success, error) {
|
export function getLogs(success, error) {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: '/api/v1/admin/logs',
|
url: '/api/v1/admin/logs',
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ export default {
|
|||||||
RECEIVED_CONFIG: null,
|
RECEIVED_CONFIG: null,
|
||||||
RECEIVED_LOGS: null,
|
RECEIVED_LOGS: null,
|
||||||
RECEIVED_SERVER_AUDITS: null,
|
RECEIVED_SERVER_AUDITS: null,
|
||||||
|
RECEIVED_SERVER_COMPLIANCE_REPORTS: null,
|
||||||
RECEIVED_ALL_TEAMS: null,
|
RECEIVED_ALL_TEAMS: null,
|
||||||
|
|
||||||
RECEIVED_LOCALE: null,
|
RECEIVED_LOCALE: null,
|
||||||
|
|||||||
@@ -125,6 +125,26 @@
|
|||||||
background-color: white;
|
background-color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.compliance__panel {
|
||||||
|
overflow: scroll;
|
||||||
|
width: 100%;
|
||||||
|
height: 400px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
margin-top: 10px;
|
||||||
|
padding: 5px;
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.audit__panel {
|
||||||
|
overflow: scroll;
|
||||||
|
width: 100%;
|
||||||
|
height: 400px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
margin-top: 10px;
|
||||||
|
padding: 5px;
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
.app__content {
|
.app__content {
|
||||||
&.admin {
|
&.admin {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
|||||||
@@ -191,6 +191,40 @@
|
|||||||
"admin.ldap.uernameAttrDesc": "The attribute in the LDAP server that will be used to populate the username field in Mattermost. This may be the same as the ID Attribute.",
|
"admin.ldap.uernameAttrDesc": "The attribute in the LDAP server that will be used to populate the username field in Mattermost. This may be the same as the ID Attribute.",
|
||||||
"admin.ldap.usernameAttrEx": "Ex \"sAMAccountName\"",
|
"admin.ldap.usernameAttrEx": "Ex \"sAMAccountName\"",
|
||||||
"admin.ldap.usernameAttrTitle": "Username Attribute:",
|
"admin.ldap.usernameAttrTitle": "Username Attribute:",
|
||||||
|
"admin.compliance.saving": "Saving Config...",
|
||||||
|
"admin.compliance.directoryExample": "Ex \"./data/\"",
|
||||||
|
"admin.compliance.noLicense": "<h4 class=\"banner__heading\">Note:</h4><p>Compliance is an enterprise feature. Your current license does not support Compliance. Click <a href=\"http://mattermost.com\" target=\"_blank\">here</a> for information and pricing on enterprise licenses.</p>",
|
||||||
|
"admin.compliance.title": "Compliance Settings",
|
||||||
|
"admin.compliance.enableTitle": "Enable Compliance:",
|
||||||
|
"admin.compliance.true": "true",
|
||||||
|
"admin.compliance.false": "false",
|
||||||
|
"admin.compliance.enableDesc": "When true, Mattermost allows compliance reporting",
|
||||||
|
"admin.compliance.directoryTitle": "Compliance Directory Location:",
|
||||||
|
"admin.compliance.directoryDescription": "Directory to which compliance reports are written. If blank, will be set to ./data/.",
|
||||||
|
"admin.compliance.enableDailyTitle": "Enable Daily Report:",
|
||||||
|
"admin.compliance.enableDesc": "When true, Mattermost will generate a daily compliance report.",
|
||||||
|
"admin.compliance.save": "Save",
|
||||||
|
"admin.compliance_reports.from": "From:",
|
||||||
|
"admin.compliance_reports.to": "To:",
|
||||||
|
"admin.compliance_reports.emails": "Emails:",
|
||||||
|
"admin.compliance_reports.keywords": "Keywords:",
|
||||||
|
"admin.compliance_table.download": "Download",
|
||||||
|
"admin.compliance_table.timestamp": "Timestamp",
|
||||||
|
"admin.compliance_table.status": "Status",
|
||||||
|
"admin.compliance_table.records": "Records",
|
||||||
|
"admin.compliance_table.type": "Type",
|
||||||
|
"admin.compliance_table.desc": "Description",
|
||||||
|
"admin.compliance_table.userId": "Requested By",
|
||||||
|
"admin.compliance_table.params": "Params",
|
||||||
|
"admin.compliance_reports.title": "Compliance Reports",
|
||||||
|
"admin.compliance_reports.desc": "Job Name:",
|
||||||
|
"admin.compliance_reports.desc_placeholder": "Ex \"Audit 445 for HR\"",
|
||||||
|
"admin.compliance_reports.from_placeholder": "Ex \"2016-03-11\"",
|
||||||
|
"admin.compliance_reports.to_placeholder": "Ex \"2016-03-15\"",
|
||||||
|
"admin.compliance_reports.emails_placeholder": "Ex \"bill@example.com, bob@example.com\"",
|
||||||
|
"admin.compliance_reports.keywords_placeholder": "Ex \"shorting stock\"",
|
||||||
|
"admin.compliance_reports.run": "Run",
|
||||||
|
"admin.compliance_reports.reload": "Reload",
|
||||||
"admin.licence.keyMigration": "If you’re migrating servers you may need to remove your license key from this server in order to install it on a new server. To start, <a href=\"http://mattermost.com\" target=\"_blank\">disable all Enterprise Edition features on this server</a>. This will enable the ability to remove the license key and downgrade this server from Enterprise Edition to Team Edition.",
|
"admin.licence.keyMigration": "If you’re migrating servers you may need to remove your license key from this server in order to install it on a new server. To start, <a href=\"http://mattermost.com\" target=\"_blank\">disable all Enterprise Edition features on this server</a>. This will enable the ability to remove the license key and downgrade this server from Enterprise Edition to Team Edition.",
|
||||||
"admin.license.chooseFile": "Choose File",
|
"admin.license.chooseFile": "Choose File",
|
||||||
"admin.license.edition": "Edition: ",
|
"admin.license.edition": "Edition: ",
|
||||||
@@ -331,6 +365,7 @@
|
|||||||
"admin.sidebar.gitlab": "GitLab Settings",
|
"admin.sidebar.gitlab": "GitLab Settings",
|
||||||
"admin.sidebar.ldap": "LDAP Settings",
|
"admin.sidebar.ldap": "LDAP Settings",
|
||||||
"admin.sidebar.license": "Edition and License",
|
"admin.sidebar.license": "Edition and License",
|
||||||
|
"admin.sidebar.compliance": "Compliance Settings",
|
||||||
"admin.sidebar.loading": "Loading",
|
"admin.sidebar.loading": "Loading",
|
||||||
"admin.sidebar.log": "Log Settings",
|
"admin.sidebar.log": "Log Settings",
|
||||||
"admin.sidebar.logs": "Logs",
|
"admin.sidebar.logs": "Logs",
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user