From 53f17b5715b653927ea6affe405b630e95334ff0 Mon Sep 17 00:00:00 2001 From: Nicola Murino Date: Thu, 19 Jan 2023 18:33:04 +0100 Subject: [PATCH] allow to disable event rules Signed-off-by: Nicola Murino --- go.mod | 10 ++--- go.sum | 18 ++++----- internal/common/common.go | 2 +- internal/common/eventmanager.go | 3 ++ internal/common/eventmanager_test.go | 4 ++ internal/common/protocol_test.go | 44 ++++++++++++++++++++ internal/dataprovider/bolt.go | 43 +++++++++++++++++--- internal/dataprovider/dataprovider.go | 2 +- internal/dataprovider/eventrule.go | 10 +++++ internal/dataprovider/memory.go | 3 ++ internal/dataprovider/mysql.go | 37 ++++++++++++++++- internal/dataprovider/pgsql.go | 42 ++++++++++++++++++- internal/dataprovider/sqlcommon.go | 8 ++-- internal/dataprovider/sqlite.go | 36 ++++++++++++++++- internal/dataprovider/sqlqueries.go | 14 +++---- internal/httpd/api_maintenance.go | 9 ++++- internal/httpd/httpd_test.go | 58 ++++++++++++++++++++++----- internal/httpd/webadmin.go | 6 +++ internal/httpdtest/httpdtest.go | 3 ++ internal/service/service.go | 3 +- openapi/openapi.yaml | 9 +++++ templates/webadmin/admin.html | 20 ++++----- templates/webadmin/eventrule.html | 10 +++++ templates/webadmin/eventrules.html | 10 ++++- 24 files changed, 342 insertions(+), 62 deletions(-) diff --git a/go.mod b/go.mod index e6332a70..03d53763 100644 --- a/go.mod +++ b/go.mod @@ -56,7 +56,7 @@ require ( github.com/shirou/gopsutil/v3 v3.22.12 github.com/spf13/afero v1.9.3 github.com/spf13/cobra v1.6.1 - github.com/spf13/viper v1.14.0 + github.com/spf13/viper v1.15.0 github.com/stretchr/testify v1.8.1 github.com/studio-b12/gowebdav v0.0.0-20221109171924-60ec5ad56012 github.com/subosito/gotenv v1.4.2 @@ -73,12 +73,12 @@ require ( golang.org/x/sys v0.4.0 golang.org/x/term v0.4.0 golang.org/x/time v0.3.0 - google.golang.org/api v0.107.0 + google.golang.org/api v0.108.0 gopkg.in/natefinch/lumberjack.v2 v2.0.0 ) require ( - cloud.google.com/go v0.108.0 // indirect + cloud.google.com/go v0.109.0 // indirect cloud.google.com/go/compute v1.15.1 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect cloud.google.com/go/iam v0.10.0 // indirect @@ -138,7 +138,6 @@ require ( github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/oklog/run v1.1.0 // indirect - github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.0.6 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect @@ -158,11 +157,10 @@ require ( golang.org/x/tools v0.5.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20230113154510-dbe35b8444a5 // indirect + google.golang.org/genproto v0.0.0-20230117162540-28d6b9783ac4 // indirect google.golang.org/grpc v1.52.0 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 0ba27bd5..f62c65d6 100644 --- a/go.sum +++ b/go.sum @@ -37,8 +37,8 @@ cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34h cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= -cloud.google.com/go v0.108.0 h1:xntQwnfn8oHGX0crLVinvHM+AhXvi3QHQIEcX/2hiWk= -cloud.google.com/go v0.108.0/go.mod h1:lNUfQqusBJp0bgAg6qrHgYFYbTB+dOiob1itwnlD33Q= +cloud.google.com/go v0.109.0 h1:38CZoKGlCnPZjGdyj0ZfpoGae0/wgNfy5F0byyxg0Gk= +cloud.google.com/go v0.109.0/go.mod h1:2sYycXt75t/CSB5R9M2wPU1tJmire7AQZTPtITcGBVE= cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4= cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw= cloud.google.com/go/accesscontextmanager v1.3.0/go.mod h1:TgCBehyr5gNMz7ZaH9xubp+CE8dkrszb4oK9CWyvD4o= @@ -1663,8 +1663,6 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= -github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= @@ -1856,8 +1854,8 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/spf13/viper v1.14.0 h1:Rg7d3Lo706X9tHsJMUjdiwMpHB7W8WnSVOssIY+JElU= -github.com/spf13/viper v1.14.0/go.mod h1:WT//axPky3FdvXHzGw33dNdXXXfFQqmEalje+egj8As= +github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU= +github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jHOQLA= github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= @@ -2577,8 +2575,8 @@ google.golang.org/api v0.99.0/go.mod h1:1YOf74vkVndF7pG6hIHuINsM7eWwpVTAfNMNiL91 google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= google.golang.org/api v0.102.0/go.mod h1:3VFl6/fzoA+qNuS1N1/VfXY4LjoXN/wzeIp7TweWwGo= google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0= -google.golang.org/api v0.107.0 h1:I2SlFjD8ZWabaIFOfeEDg3pf0BHJDh6iYQ1ic3Yu/UU= -google.golang.org/api v0.107.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.108.0 h1:WVBc/faN0DkKtR43Q/7+tPny9ZoLZdIiAyG5Q9vFClg= +google.golang.org/api v0.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -2712,8 +2710,8 @@ google.golang.org/genproto v0.0.0-20221109142239-94d6d90a7d66/go.mod h1:rZS5c/ZV google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= google.golang.org/genproto v0.0.0-20221201204527-e3fa12d562f3/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20230113154510-dbe35b8444a5 h1:wJT65XLOzhpSPCdAmmKfz94SlmnQzDzjm3Cj9k3fsXY= -google.golang.org/genproto v0.0.0-20230113154510-dbe35b8444a5/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230117162540-28d6b9783ac4 h1:yF0uHwqqYt2tIL2F4hxRWA1ZFX43SEunWAK8MnQiclk= +google.golang.org/genproto v0.0.0-20230117162540-28d6b9783ac4/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= diff --git a/internal/common/common.go b/internal/common/common.go index 0714baa5..df51a6b1 100644 --- a/internal/common/common.go +++ b/internal/common/common.go @@ -949,7 +949,7 @@ func (conns *ActiveConnections) Remove(connectionID string) { return } - logger.Warn(logSender, "", "connection id %q to remove not found!", connectionID) + logger.Debug(logSender, "", "connection id %q to remove not found!", connectionID) } // Close closes an active connection. diff --git a/internal/common/eventmanager.go b/internal/common/eventmanager.go index 634f747c..1e103c84 100644 --- a/internal/common/eventmanager.go +++ b/internal/common/eventmanager.go @@ -197,6 +197,9 @@ func (r *eventRulesContainer) addUpdateRuleInternal(rule dataprovider.EventRule) } return } + if rule.Status != 1 { + return + } switch rule.Trigger { case dataprovider.EventTriggerFsEvent: r.FsEvents = append(r.FsEvents, rule) diff --git a/internal/common/eventmanager_test.go b/internal/common/eventmanager_test.go index a35ca01a..aa3522fd 100644 --- a/internal/common/eventmanager_test.go +++ b/internal/common/eventmanager_test.go @@ -320,6 +320,7 @@ func TestEventManager(t *testing.T) { assert.NoError(t, err) rule := &dataprovider.EventRule{ Name: "rule", + Status: 1, Trigger: dataprovider.EventTriggerFsEvent, Conditions: dataprovider.EventConditions{ FsEvents: []string{operationUpload}, @@ -595,6 +596,7 @@ func TestEventManagerErrors(t *testing.T) { // rule with invalid trigger eventManager.addUpdateRuleInternal(dataprovider.EventRule{ Name: "test rule", + Status: 1, Trigger: -1, }) @@ -606,6 +608,7 @@ func TestEventManagerErrors(t *testing.T) { // rule with invalid cronspec eventManager.addUpdateRuleInternal(dataprovider.EventRule{ Name: "test rule", + Status: 1, Trigger: dataprovider.EventTriggerSchedule, Conditions: dataprovider.EventConditions{ Schedules: []dataprovider.Schedule{ @@ -1602,6 +1605,7 @@ func TestScheduledActions(t *testing.T) { assert.NoError(t, err) rule := &dataprovider.EventRule{ Name: "rule", + Status: 1, Trigger: dataprovider.EventTriggerSchedule, Conditions: dataprovider.EventConditions{ Schedules: []dataprovider.Schedule{ diff --git a/internal/common/protocol_test.go b/internal/common/protocol_test.go index bd405b8c..5a99b2a6 100644 --- a/internal/common/protocol_test.go +++ b/internal/common/protocol_test.go @@ -22,6 +22,7 @@ import ( "errors" "fmt" "io" + "io/fs" "math" "net" "net/http" @@ -3461,6 +3462,7 @@ func TestEventRule(t *testing.T) { r1 := dataprovider.EventRule{ Name: "test rule1", + Status: 1, Trigger: dataprovider.EventTriggerFsEvent, Conditions: dataprovider.EventConditions{ FsEvents: []string{"upload"}, @@ -3514,6 +3516,7 @@ func TestEventRule(t *testing.T) { r2 := dataprovider.EventRule{ Name: "test rule2", + Status: 1, Trigger: dataprovider.EventTriggerFsEvent, Conditions: dataprovider.EventConditions{ FsEvents: []string{"download"}, @@ -3548,6 +3551,7 @@ func TestEventRule(t *testing.T) { r3 := dataprovider.EventRule{ Name: "test rule3", + Status: 1, Trigger: dataprovider.EventTriggerProviderEvent, Conditions: dataprovider.EventConditions{ ProviderEvents: []string{"delete"}, @@ -3810,6 +3814,7 @@ func TestEventRuleProviderEvents(t *testing.T) { r := dataprovider.EventRule{ Name: "rule", + Status: 1, Trigger: dataprovider.EventTriggerProviderEvent, Conditions: dataprovider.EventConditions{ ProviderEvents: []string{"update"}, @@ -3991,6 +3996,7 @@ func TestEventRuleFsActions(t *testing.T) { r1 := dataprovider.EventRule{ Name: "r1", + Status: 1, Trigger: dataprovider.EventTriggerProviderEvent, Conditions: dataprovider.EventConditions{ ProviderEvents: []string{"add"}, @@ -4006,6 +4012,7 @@ func TestEventRuleFsActions(t *testing.T) { } r2 := dataprovider.EventRule{ Name: "r2", + Status: 1, Trigger: dataprovider.EventTriggerFsEvent, Conditions: dataprovider.EventConditions{ FsEvents: []string{"upload"}, @@ -4030,6 +4037,7 @@ func TestEventRuleFsActions(t *testing.T) { } r3 := dataprovider.EventRule{ Name: "r3", + Status: 1, Trigger: dataprovider.EventTriggerFsEvent, Conditions: dataprovider.EventConditions{ FsEvents: []string{"mkdir"}, @@ -4051,6 +4059,7 @@ func TestEventRuleFsActions(t *testing.T) { } r4 := dataprovider.EventRule{ Name: "r4", + Status: 1, Trigger: dataprovider.EventTriggerFsEvent, Conditions: dataprovider.EventConditions{ FsEvents: []string{"rmdir"}, @@ -4066,6 +4075,7 @@ func TestEventRuleFsActions(t *testing.T) { } r5 := dataprovider.EventRule{ Name: "r5", + Status: 1, Trigger: dataprovider.EventTriggerProviderEvent, Conditions: dataprovider.EventConditions{ ProviderEvents: []string{"add"}, @@ -4216,6 +4226,7 @@ func TestEventRulePreDelete(t *testing.T) { assert.NoError(t, err, string(resp)) r1 := dataprovider.EventRule{ Name: "rule1", + Status: 1, Trigger: dataprovider.EventTriggerFsEvent, Conditions: dataprovider.EventConditions{ FsEvents: []string{"pre-delete"}, @@ -4347,6 +4358,7 @@ func TestEventRulePreDownloadUpload(t *testing.T) { assert.NoError(t, err, string(resp)) r1 := dataprovider.EventRule{ Name: "rule1", + Status: 1, Trigger: dataprovider.EventTriggerFsEvent, Conditions: dataprovider.EventConditions{ FsEvents: []string{"pre-download", "pre-upload"}, @@ -4386,7 +4398,20 @@ func TestEventRulePreDownloadUpload(t *testing.T) { assert.Equal(t, int(100), n) err = f.Close() assert.NoError(t, err) + // disable the rule + rule1.Status = 0 + _, _, err = httpdtest.UpdateEventRule(rule1, http.StatusOK) + assert.NoError(t, err) + err = client.RemoveDirectory(testDir) + assert.NoError(t, err) + err = client.Remove(testFileName) + assert.NoError(t, err) + err = writeSFTPFile(testFileName, 100, client) + assert.NoError(t, err) + _, err = client.Stat(testDir) + assert.ErrorIs(t, err, fs.ErrNotExist) // now update the rule so that it will always fail + rule1.Status = 1 rule1.Actions = []dataprovider.EventAction{ { BaseEventAction: dataprovider.BaseEventAction{ @@ -4441,6 +4466,7 @@ func TestFsActionCopy(t *testing.T) { r1 := dataprovider.EventRule{ Name: "rule1", + Status: 1, Trigger: dataprovider.EventTriggerFsEvent, Conditions: dataprovider.EventConditions{ FsEvents: []string{"upload"}, @@ -4520,6 +4546,7 @@ func TestEventFsActionsGroupFilters(t *testing.T) { r1 := dataprovider.EventRule{ Name: "rule1", + Status: 1, Trigger: dataprovider.EventTriggerFsEvent, Conditions: dataprovider.EventConditions{ FsEvents: []string{"upload"}, @@ -4657,6 +4684,7 @@ func TestBackupAsAttachment(t *testing.T) { r1 := dataprovider.EventRule{ Name: "test rule certificate", + Status: 1, Trigger: dataprovider.EventTriggerCertificate, Actions: []dataprovider.EventAction{ { @@ -4738,6 +4766,7 @@ func TestEventActionHTTPMultipart(t *testing.T) { assert.NoError(t, err, string(resp)) r1 := dataprovider.EventRule{ Name: "test http multipart", + Status: 1, Trigger: dataprovider.EventTriggerFsEvent, Conditions: dataprovider.EventConditions{ FsEvents: []string{"upload"}, @@ -4813,6 +4842,7 @@ func TestEventActionCompress(t *testing.T) { assert.NoError(t, err) r1 := dataprovider.EventRule{ Name: "test compress", + Status: 1, Trigger: dataprovider.EventTriggerFsEvent, Conditions: dataprovider.EventConditions{ FsEvents: []string{"upload"}, @@ -4981,6 +5011,7 @@ func TestEventActionCompressQuotaErrors(t *testing.T) { assert.NoError(t, err) r1 := dataprovider.EventRule{ Name: "test compress", + Status: 1, Trigger: dataprovider.EventTriggerFsEvent, Conditions: dataprovider.EventConditions{ FsEvents: []string{"rename"}, @@ -5126,6 +5157,7 @@ func TestEventActionCompressQuotaFolder(t *testing.T) { assert.NoError(t, err) r1 := dataprovider.EventRule{ Name: "test compress", + Status: 1, Trigger: dataprovider.EventTriggerFsEvent, Conditions: dataprovider.EventConditions{ FsEvents: []string{"upload"}, @@ -5246,6 +5278,7 @@ func TestEventActionCompressErrors(t *testing.T) { assert.NoError(t, err) r1 := dataprovider.EventRule{ Name: "test compress", + Status: 1, Trigger: dataprovider.EventTriggerFsEvent, Conditions: dataprovider.EventConditions{ FsEvents: []string{"upload"}, @@ -5383,6 +5416,7 @@ func TestEventActionEmailAttachments(t *testing.T) { assert.NoError(t, err) r1 := dataprovider.EventRule{ Name: "test email with attachment", + Status: 1, Trigger: dataprovider.EventTriggerFsEvent, Conditions: dataprovider.EventConditions{ FsEvents: []string{"upload"}, @@ -5536,6 +5570,7 @@ func TestEventActionsRetentionReports(t *testing.T) { assert.NoError(t, err) r1 := dataprovider.EventRule{ Name: "test rule1", + Status: 1, Trigger: dataprovider.EventTriggerFsEvent, Conditions: dataprovider.EventConditions{ FsEvents: []string{"upload"}, @@ -5729,6 +5764,7 @@ func TestEventRuleFirstUploadDownloadActions(t *testing.T) { assert.NoError(t, err) r1 := dataprovider.EventRule{ Name: "test first upload rule", + Status: 1, Trigger: dataprovider.EventTriggerFsEvent, Conditions: dataprovider.EventConditions{ FsEvents: []string{"first-upload"}, @@ -5746,6 +5782,7 @@ func TestEventRuleFirstUploadDownloadActions(t *testing.T) { assert.NoError(t, err) r2 := dataprovider.EventRule{ Name: "test first download rule", + Status: 1, Trigger: dataprovider.EventTriggerFsEvent, Conditions: dataprovider.EventConditions{ FsEvents: []string{"first-download"}, @@ -5859,6 +5896,7 @@ func TestEventRuleRenameEvent(t *testing.T) { assert.NoError(t, err) r1 := dataprovider.EventRule{ Name: "test rename rule", + Status: 1, Trigger: dataprovider.EventTriggerFsEvent, Conditions: dataprovider.EventConditions{ FsEvents: []string{"rename"}, @@ -5948,6 +5986,7 @@ func TestEventRuleCertificate(t *testing.T) { r1 := dataprovider.EventRule{ Name: "test rule certificate", + Status: 1, Trigger: dataprovider.EventTriggerCertificate, Actions: []dataprovider.EventAction{ { @@ -5962,6 +6001,7 @@ func TestEventRuleCertificate(t *testing.T) { assert.NoError(t, err) r2 := dataprovider.EventRule{ Name: "test rule 2", + Status: 1, Trigger: dataprovider.EventTriggerCertificate, Actions: []dataprovider.EventAction{ { @@ -6082,6 +6122,7 @@ func TestEventRuleIPBlocked(t *testing.T) { r1 := dataprovider.EventRule{ Name: "test rule ip blocked", + Status: 1, Trigger: dataprovider.EventTriggerIPBlocked, Actions: []dataprovider.EventAction{ { @@ -6096,6 +6137,7 @@ func TestEventRuleIPBlocked(t *testing.T) { assert.NoError(t, err) r2 := dataprovider.EventRule{ Name: "test rule 2", + Status: 1, Trigger: dataprovider.EventTriggerIPBlocked, Actions: []dataprovider.EventAction{ { @@ -6214,6 +6256,7 @@ func TestEventRulePasswordExpiration(t *testing.T) { r1 := dataprovider.EventRule{ Name: "rule1", + Status: 1, Trigger: dataprovider.EventTriggerFsEvent, Conditions: dataprovider.EventConditions{ FsEvents: []string{"mkdir"}, @@ -7017,6 +7060,7 @@ func TestSFTPLoopError(t *testing.T) { assert.NoError(t, err) r1 := dataprovider.EventRule{ Name: "rule1", + Status: 1, Trigger: dataprovider.EventTriggerProviderEvent, Conditions: dataprovider.EventConditions{ ProviderEvents: []string{"update"}, diff --git a/internal/dataprovider/bolt.go b/internal/dataprovider/bolt.go index 41706ab1..5fc72e41 100644 --- a/internal/dataprovider/bolt.go +++ b/internal/dataprovider/bolt.go @@ -35,7 +35,7 @@ import ( ) const ( - boltDatabaseVersion = 25 + boltDatabaseVersion = 26 ) var ( @@ -2833,10 +2833,41 @@ func (p *BoltProvider) migrateDatabase() error { providerLog(logger.LevelError, "%v", err) logger.ErrorToConsole("%v", err) return err - case version == 23, version == 24: - logger.InfoToConsole(fmt.Sprintf("updating database schema version: %d -> 25", version)) - providerLog(logger.LevelInfo, "updating database schema version: %d -> 25", version) - return updateBoltDatabaseVersion(p.dbHandle, 25) + case version == 23, version == 24, version == 25: + logger.InfoToConsole("updating database schema version: %d -> 26", version) + providerLog(logger.LevelInfo, "updating database schema version: %d -> 26", version) + err := p.dbHandle.Update(func(tx *bolt.Tx) error { + rules, err := p.dumpEventRules() + if err != nil { + return err + } + bucket, err := p.getRulesBucket(tx) + if err != nil { + return err + } + for _, rule := range rules { + rule := rule // pin + if rule.Status == 1 { + continue + } + logger.InfoToConsole("setting status to active for rule %q", rule.Name) + providerLog(logger.LevelInfo, "setting status to 1 for rule %q", rule.Name) + rule.Status = 1 + rule.UpdatedAt = util.GetTimeAsMsSinceEpoch(time.Now()) + buf, err := json.Marshal(rule) + if err != nil { + return err + } + if err := bucket.Put([]byte(rule.Name), buf); err != nil { + return err + } + } + return nil + }) + if err != nil { + return err + } + return updateBoltDatabaseVersion(p.dbHandle, 26) default: if version > boltDatabaseVersion { providerLog(logger.LevelError, "database schema version %d is newer than the supported one: %d", version, @@ -2858,7 +2889,7 @@ func (p *BoltProvider) revertDatabase(targetVersion int) error { return errors.New("current version match target version, nothing to do") } switch dbVersion.Version { - case 24, 25: + case 24, 25, 26: logger.InfoToConsole("downgrading database schema version: %d -> 23", dbVersion.Version) providerLog(logger.LevelInfo, "downgrading database schema version: %d -> 23", dbVersion.Version) err := p.dbHandle.Update(func(tx *bolt.Tx) error { diff --git a/internal/dataprovider/dataprovider.go b/internal/dataprovider/dataprovider.go index 91de4453..fb1539f0 100644 --- a/internal/dataprovider/dataprovider.go +++ b/internal/dataprovider/dataprovider.go @@ -87,7 +87,7 @@ const ( CockroachDataProviderName = "cockroachdb" // DumpVersion defines the version for the dump. // For restore/load we support the current version and the previous one - DumpVersion = 14 + DumpVersion = 15 argonPwdPrefix = "$argon2id$" bcryptPwdPrefix = "$2a$" diff --git a/internal/dataprovider/eventrule.go b/internal/dataprovider/eventrule.go index 8680a782..118eae6e 100644 --- a/internal/dataprovider/eventrule.go +++ b/internal/dataprovider/eventrule.go @@ -1313,6 +1313,8 @@ type EventRule struct { ID int64 `json:"id"` // Rule name Name string `json:"name"` + // 1 enabled, 0 disabled + Status int `json:"status"` // optional description Description string `json:"description,omitempty"` // Creation time as unix timestamp in milliseconds @@ -1338,6 +1340,7 @@ func (r *EventRule) getACopy() EventRule { return EventRule{ ID: r.ID, Name: r.Name, + Status: r.Status, Description: r.Description, CreatedAt: r.CreatedAt, UpdatedAt: r.UpdatedAt, @@ -1371,10 +1374,17 @@ func (r *EventRule) GetActionsAsString() string { return strings.Join(actions, ",") } +func (r *EventRule) isStatusValid() bool { + return r.Status >= 0 && r.Status <= 1 +} + func (r *EventRule) validate() error { if r.Name == "" { return util.NewValidationError("name is mandatory") } + if !r.isStatusValid() { + return util.NewValidationError(fmt.Sprintf("invalid event rule status: %d", r.Status)) + } if !isEventTriggerValid(r.Trigger) { return util.NewValidationError(fmt.Sprintf("invalid event rule trigger: %d", r.Trigger)) } diff --git a/internal/dataprovider/memory.go b/internal/dataprovider/memory.go index b8a265fb..f3ade7da 100644 --- a/internal/dataprovider/memory.go +++ b/internal/dataprovider/memory.go @@ -2825,6 +2825,9 @@ func (p *MemoryProvider) restoreEventRules(dump BackupData) error { for _, rule := range dump.EventRules { r, err := p.eventRuleExists(rule.Name) rule := rule // pin + if dump.Version < 15 { + rule.Status = 1 + } if err == nil { rule.ID = r.ID err = UpdateEventRule(&rule, ActionExecutorSystem, "", "") diff --git a/internal/dataprovider/mysql.go b/internal/dataprovider/mysql.go index 79d1a855..dbca8dd0 100644 --- a/internal/dataprovider/mysql.go +++ b/internal/dataprovider/mysql.go @@ -186,6 +186,9 @@ const ( mysqlV25SQL = "ALTER TABLE `{{users}}` ADD COLUMN `last_password_change` bigint DEFAULT 0 NOT NULL; " + "ALTER TABLE `{{users}}` ALTER COLUMN `last_password_change` DROP DEFAULT; " mysqlV25DownSQL = "ALTER TABLE `{{users}}` DROP COLUMN `last_password_change`; " + mysqlV26SQL = "ALTER TABLE `{{events_rules}}` ADD COLUMN `status` integer DEFAULT 1 NOT NULL; " + + "ALTER TABLE `{{events_rules}}` ALTER COLUMN `status` DROP DEFAULT; " + mysqlV26DownSQL = "ALTER TABLE `{{events_rules}}` DROP COLUMN `status`; " ) // MySQLProvider defines the auth provider for MySQL/MariaDB database @@ -744,6 +747,8 @@ func (p *MySQLProvider) migrateDatabase() error { //nolint:dupl return updateMySQLDatabaseFromV23(p.dbHandle) case version == 24: return updateMySQLDatabaseFromV24(p.dbHandle) + case version == 25: + return updateMySQLDatabaseFromV25(p.dbHandle) default: if version > sqlDatabaseVersion { providerLog(logger.LevelError, "database schema version %d is newer than the supported one: %d", version, @@ -770,6 +775,8 @@ func (p *MySQLProvider) revertDatabase(targetVersion int) error { return downgradeMySQLDatabaseFromV24(p.dbHandle) case 25: return downgradeMySQLDatabaseFromV25(p.dbHandle) + case 26: + return downgradeMySQLDatabaseFromV26(p.dbHandle) default: return fmt.Errorf("database schema version not handled: %d", dbVersion.Version) } @@ -788,7 +795,14 @@ func updateMySQLDatabaseFromV23(dbHandle *sql.DB) error { } func updateMySQLDatabaseFromV24(dbHandle *sql.DB) error { - return updateMySQLDatabaseFrom24To25(dbHandle) + if err := updateMySQLDatabaseFrom24To25(dbHandle); err != nil { + return err + } + return updateMySQLDatabaseFromV25(dbHandle) +} + +func updateMySQLDatabaseFromV25(dbHandle *sql.DB) error { + return updateMySQLDatabaseFrom25To26(dbHandle) } func downgradeMySQLDatabaseFromV24(dbHandle *sql.DB) error { @@ -802,6 +816,13 @@ func downgradeMySQLDatabaseFromV25(dbHandle *sql.DB) error { return downgradeMySQLDatabaseFromV24(dbHandle) } +func downgradeMySQLDatabaseFromV26(dbHandle *sql.DB) error { + if err := downgradeMySQLDatabaseFrom26To25(dbHandle); err != nil { + return err + } + return downgradeMySQLDatabaseFromV25(dbHandle) +} + func updateMySQLDatabaseFrom23To24(dbHandle *sql.DB) error { logger.InfoToConsole("updating database schema version: 23 -> 24") providerLog(logger.LevelInfo, "updating database schema version: 23 -> 24") @@ -819,6 +840,13 @@ func updateMySQLDatabaseFrom24To25(dbHandle *sql.DB) error { return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, strings.Split(sql, ";"), 25, true) } +func updateMySQLDatabaseFrom25To26(dbHandle *sql.DB) error { + logger.InfoToConsole("updating database schema version: 25 -> 26") + providerLog(logger.LevelInfo, "updating database schema version: 25 -> 26") + sql := strings.ReplaceAll(mysqlV26SQL, "{{events_rules}}", sqlTableEventsRules) + return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, strings.Split(sql, ";"), 26, true) +} + func downgradeMySQLDatabaseFrom24To23(dbHandle *sql.DB) error { logger.InfoToConsole("downgrading database schema version: 24 -> 23") providerLog(logger.LevelInfo, "downgrading database schema version: 24 -> 23") @@ -835,3 +863,10 @@ func downgradeMySQLDatabaseFrom25To24(dbHandle *sql.DB) error { sql := strings.ReplaceAll(mysqlV25DownSQL, "{{users}}", sqlTableUsers) return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, strings.Split(sql, ";"), 24, false) } + +func downgradeMySQLDatabaseFrom26To25(dbHandle *sql.DB) error { + logger.InfoToConsole("downgrading database schema version: 26 -> 25") + providerLog(logger.LevelInfo, "downgrading database schema version: 26 -> 25") + sql := strings.ReplaceAll(mysqlV26DownSQL, "{{events_rules}}", sqlTableEventsRules) + return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, strings.Split(sql, ";"), 25, false) +} diff --git a/internal/dataprovider/pgsql.go b/internal/dataprovider/pgsql.go index ead72708..0f9bb387 100644 --- a/internal/dataprovider/pgsql.go +++ b/internal/dataprovider/pgsql.go @@ -198,6 +198,10 @@ DROP TABLE "{{roles}}" CASCADE; ALTER TABLE "{{users}}" ALTER COLUMN "last_password_change" DROP DEFAULT; ` pgsqlV25DownSQL = `ALTER TABLE "{{users}}" DROP COLUMN "last_password_change" CASCADE;` + pgsqlV26SQL = `ALTER TABLE "{{events_rules}}" ADD COLUMN "status" integer DEFAULT 1 NOT NULL; +ALTER TABLE "{{events_rules}}" ALTER COLUMN "status" DROP DEFAULT; +` + pgsqlV26DownSQL = `ALTER TABLE "{{events_rules}}" DROP COLUMN "status" CASCADE;` ) // PGSQLProvider defines the auth provider for PostgreSQL database @@ -715,6 +719,8 @@ func (p *PGSQLProvider) migrateDatabase() error { //nolint:dupl return updatePgSQLDatabaseFromV23(p.dbHandle) case version == 24: return updatePgSQLDatabaseFromV24(p.dbHandle) + case version == 25: + return updatePgSQLDatabaseFromV25(p.dbHandle) default: if version > sqlDatabaseVersion { providerLog(logger.LevelError, "database schema version %d is newer than the supported one: %d", version, @@ -741,6 +747,8 @@ func (p *PGSQLProvider) revertDatabase(targetVersion int) error { return downgradePgSQLDatabaseFromV24(p.dbHandle) case 25: return downgradePgSQLDatabaseFromV25(p.dbHandle) + case 26: + return downgradePgSQLDatabaseFromV26(p.dbHandle) default: return fmt.Errorf("database schema version not handled: %d", dbVersion.Version) } @@ -759,7 +767,14 @@ func updatePgSQLDatabaseFromV23(dbHandle *sql.DB) error { } func updatePgSQLDatabaseFromV24(dbHandle *sql.DB) error { - return updatePgSQLDatabaseFrom24To25(dbHandle) + if err := updatePgSQLDatabaseFrom24To25(dbHandle); err != nil { + return err + } + return updatePgSQLDatabaseFromV25(dbHandle) +} + +func updatePgSQLDatabaseFromV25(dbHandle *sql.DB) error { + return updatePgSQLDatabaseFrom25To26(dbHandle) } func downgradePgSQLDatabaseFromV24(dbHandle *sql.DB) error { @@ -773,6 +788,13 @@ func downgradePgSQLDatabaseFromV25(dbHandle *sql.DB) error { return downgradePgSQLDatabaseFromV24(dbHandle) } +func downgradePgSQLDatabaseFromV26(dbHandle *sql.DB) error { + if err := downgradePgSQLDatabaseFrom26To25(dbHandle); err != nil { + return err + } + return downgradePgSQLDatabaseFromV25(dbHandle) +} + func updatePgSQLDatabaseFrom23To24(dbHandle *sql.DB) error { logger.InfoToConsole("updating database schema version: 23 -> 24") providerLog(logger.LevelInfo, "updating database schema version: 23 -> 24") @@ -794,6 +816,17 @@ func updatePgSQLDatabaseFrom24To25(dbHandle *sql.DB) error { return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, []string{sql}, 25, true) } +func updatePgSQLDatabaseFrom25To26(dbHandle *sql.DB) error { + logger.InfoToConsole("updating database schema version: 25 -> 26") + providerLog(logger.LevelInfo, "updating database schema version: 25 -> 26") + sql := pgsqlV26SQL + if config.Driver == CockroachDataProviderName { + sql = strings.ReplaceAll(sql, `ALTER TABLE "{{events_rules}}" ALTER COLUMN "status" DROP DEFAULT;`, "") + } + sql = strings.ReplaceAll(sql, "{{events_rules}}", sqlTableEventsRules) + return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, []string{sql}, 26, true) +} + func downgradePgSQLDatabaseFrom24To23(dbHandle *sql.DB) error { logger.InfoToConsole("downgrading database schema version: 24 -> 23") providerLog(logger.LevelInfo, "downgrading database schema version: 24 -> 23") @@ -810,3 +843,10 @@ func downgradePgSQLDatabaseFrom25To24(dbHandle *sql.DB) error { sql := strings.ReplaceAll(pgsqlV25DownSQL, "{{users}}", sqlTableUsers) return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, []string{sql}, 24, false) } + +func downgradePgSQLDatabaseFrom26To25(dbHandle *sql.DB) error { + logger.InfoToConsole("downgrading database schema version: 26 -> 25") + providerLog(logger.LevelInfo, "downgrading database schema version: 26 -> 25") + sql := strings.ReplaceAll(pgsqlV26DownSQL, "{{events_rules}}", sqlTableEventsRules) + return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, []string{sql}, 25, false) +} diff --git a/internal/dataprovider/sqlcommon.go b/internal/dataprovider/sqlcommon.go index d999534f..4fd9ea39 100644 --- a/internal/dataprovider/sqlcommon.go +++ b/internal/dataprovider/sqlcommon.go @@ -34,7 +34,7 @@ import ( ) const ( - sqlDatabaseVersion = 25 + sqlDatabaseVersion = 26 defaultSQLQueryTimeout = 10 * time.Second longSQLQueryTimeout = 60 * time.Second ) @@ -1853,7 +1853,7 @@ func getEventRuleFromDbRow(row sqlScanner) (EventRule, error) { var conditions []byte err := row.Scan(&rule.ID, &rule.Name, &description, &rule.CreatedAt, &rule.UpdatedAt, &rule.Trigger, - &conditions, &rule.DeletedAt) + &conditions, &rule.DeletedAt, &rule.Status) if err != nil { if errors.Is(err, sql.ErrNoRows) { return rule, util.NewRecordNotFoundError(err.Error()) @@ -3353,7 +3353,7 @@ func sqlCommonAddEventRule(rule *EventRule, dbHandle *sql.DB) error { } q := getAddEventRuleQuery() _, err := tx.ExecContext(ctx, q, rule.Name, rule.Description, util.GetTimeAsMsSinceEpoch(time.Now()), - util.GetTimeAsMsSinceEpoch(time.Now()), rule.Trigger, string(conditions)) + util.GetTimeAsMsSinceEpoch(time.Now()), rule.Trigger, string(conditions), rule.Status) if err != nil { return err } @@ -3375,7 +3375,7 @@ func sqlCommonUpdateEventRule(rule *EventRule, dbHandle *sql.DB) error { return sqlCommonExecuteTx(ctx, dbHandle, func(tx *sql.Tx) error { q := getUpdateEventRuleQuery() _, err := tx.ExecContext(ctx, q, rule.Description, util.GetTimeAsMsSinceEpoch(time.Now()), - rule.Trigger, string(conditions), rule.Name) + rule.Trigger, string(conditions), rule.Status, rule.Name) if err != nil { return err } diff --git a/internal/dataprovider/sqlite.go b/internal/dataprovider/sqlite.go index 296f08e3..bee4df3e 100644 --- a/internal/dataprovider/sqlite.go +++ b/internal/dataprovider/sqlite.go @@ -177,6 +177,8 @@ DROP TABLE "{{roles}}"; ` sqliteV25SQL = `ALTER TABLE "{{users}}" ADD COLUMN "last_password_change" bigint DEFAULT 0 NOT NULL;` sqliteV25DownSQL = `ALTER TABLE "{{users}}" DROP COLUMN "last_password_change";` + sqliteV26SQL = `ALTER TABLE "{{events_rules}}" ADD COLUMN "status" integer DEFAULT 1 NOT NULL;` + sqliteV26DownSQL = `ALTER TABLE "{{events_rules}}" DROP COLUMN "status";` ) // SQLiteProvider defines the auth provider for SQLite database @@ -673,6 +675,8 @@ func (p *SQLiteProvider) migrateDatabase() error { //nolint:dupl return updateSQLiteDatabaseFromV23(p.dbHandle) case version == 24: return updateSQLiteDatabaseFromV24(p.dbHandle) + case version == 25: + return updateSQLiteDatabaseFromV25(p.dbHandle) default: if version > sqlDatabaseVersion { providerLog(logger.LevelError, "database schema version %d is newer than the supported one: %d", version, @@ -699,6 +703,8 @@ func (p *SQLiteProvider) revertDatabase(targetVersion int) error { return downgradeSQLiteDatabaseFromV24(p.dbHandle) case 25: return downgradeSQLiteDatabaseFromV25(p.dbHandle) + case 26: + return downgradeSQLiteDatabaseFromV26(p.dbHandle) default: return fmt.Errorf("database schema version not handled: %d", dbVersion.Version) } @@ -717,7 +723,14 @@ func updateSQLiteDatabaseFromV23(dbHandle *sql.DB) error { } func updateSQLiteDatabaseFromV24(dbHandle *sql.DB) error { - return updateSQLiteDatabaseFrom24To25(dbHandle) + if err := updateSQLiteDatabaseFrom24To25(dbHandle); err != nil { + return err + } + return updateSQLiteDatabaseFromV25(dbHandle) +} + +func updateSQLiteDatabaseFromV25(dbHandle *sql.DB) error { + return updateSQLiteDatabaseFrom25To26(dbHandle) } func downgradeSQLiteDatabaseFromV24(dbHandle *sql.DB) error { @@ -731,6 +744,13 @@ func downgradeSQLiteDatabaseFromV25(dbHandle *sql.DB) error { return downgradeSQLiteDatabaseFromV24(dbHandle) } +func downgradeSQLiteDatabaseFromV26(dbHandle *sql.DB) error { + if err := downgradeSQLiteDatabaseFrom26To25(dbHandle); err != nil { + return err + } + return downgradeSQLiteDatabaseFromV25(dbHandle) +} + func updateSQLiteDatabaseFrom23To24(dbHandle *sql.DB) error { logger.InfoToConsole("updating database schema version: 23 -> 24") providerLog(logger.LevelInfo, "updating database schema version: 23 -> 24") @@ -748,6 +768,13 @@ func updateSQLiteDatabaseFrom24To25(dbHandle *sql.DB) error { return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, []string{sql}, 25, true) } +func updateSQLiteDatabaseFrom25To26(dbHandle *sql.DB) error { + logger.InfoToConsole("updating database schema version: 25 -> 26") + providerLog(logger.LevelInfo, "updating database schema version: 25 -> 26") + sql := strings.ReplaceAll(sqliteV26SQL, "{{events_rules}}", sqlTableEventsRules) + return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, []string{sql}, 26, true) +} + func downgradeSQLiteDatabaseFrom24To23(dbHandle *sql.DB) error { logger.InfoToConsole("downgrading database schema version: 24 -> 23") providerLog(logger.LevelInfo, "downgrading database schema version: 24 -> 23") @@ -765,6 +792,13 @@ func downgradeSQLiteDatabaseFrom25To24(dbHandle *sql.DB) error { return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, []string{sql}, 24, false) } +func downgradeSQLiteDatabaseFrom26To25(dbHandle *sql.DB) error { + logger.InfoToConsole("downgrading database schema version: 26 -> 25") + providerLog(logger.LevelInfo, "downgrading database schema version: 26 -> 25") + sql := strings.ReplaceAll(sqliteV26DownSQL, "{{events_rules}}", sqlTableEventsRules) + return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, []string{sql}, 25, false) +} + /*func setPragmaFK(dbHandle *sql.DB, value string) error { ctx, cancel := context.WithTimeout(context.Background(), longSQLQueryTimeout) defer cancel() diff --git a/internal/dataprovider/sqlqueries.go b/internal/dataprovider/sqlqueries.go index 07191294..747f9dc0 100644 --- a/internal/dataprovider/sqlqueries.go +++ b/internal/dataprovider/sqlqueries.go @@ -61,10 +61,10 @@ func getSQLQuotedName(name string) string { func getSelectEventRuleFields() string { if config.Driver == MySQLDataProviderName { - return "id,name,description,created_at,updated_at,`trigger`,conditions,deleted_at" + return "id,name,description,created_at,updated_at,`trigger`,conditions,deleted_at,status" } - return `id,name,description,created_at,updated_at,"trigger",conditions,deleted_at` + return `id,name,description,created_at,updated_at,"trigger",conditions,deleted_at,status` } func getCoalesceDefaultForRole(role string) string { @@ -973,16 +973,16 @@ func getEventRulesByNameQuery() string { } func getAddEventRuleQuery() string { - return fmt.Sprintf(`INSERT INTO %s (name,description,created_at,updated_at,%s,conditions,deleted_at) - VALUES (%s,%s,%s,%s,%s,%s,0)`, + return fmt.Sprintf(`INSERT INTO %s (name,description,created_at,updated_at,%s,conditions,deleted_at,status) + VALUES (%s,%s,%s,%s,%s,%s,0,%s)`, sqlTableEventsRules, getSQLQuotedName("trigger"), sqlPlaceholders[0], sqlPlaceholders[1], sqlPlaceholders[2], - sqlPlaceholders[3], sqlPlaceholders[4], sqlPlaceholders[5]) + sqlPlaceholders[3], sqlPlaceholders[4], sqlPlaceholders[5], sqlPlaceholders[6]) } func getUpdateEventRuleQuery() string { - return fmt.Sprintf(`UPDATE %s SET description=%s,updated_at=%s,%s=%s,conditions=%s WHERE name = %s`, + return fmt.Sprintf(`UPDATE %s SET description=%s,updated_at=%s,%s=%s,conditions=%s,status=%s WHERE name = %s`, sqlTableEventsRules, sqlPlaceholders[0], sqlPlaceholders[1], getSQLQuotedName("trigger"), sqlPlaceholders[2], - sqlPlaceholders[3], sqlPlaceholders[4]) + sqlPlaceholders[3], sqlPlaceholders[4], sqlPlaceholders[5]) } func getDeleteEventRuleQuery(softDelete bool) string { diff --git a/internal/httpd/api_maintenance.go b/internal/httpd/api_maintenance.go index 69ff902a..04cb844c 100644 --- a/internal/httpd/api_maintenance.go +++ b/internal/httpd/api_maintenance.go @@ -212,7 +212,7 @@ func restoreBackup(content []byte, inputFile string, scanQuota, mode int, execut return err } - if err = RestoreEventRules(dump.EventRules, inputFile, mode, executor, ipAddress, role); err != nil { + if err = RestoreEventRules(dump.EventRules, inputFile, mode, executor, ipAddress, role, dump.Version); err != nil { return err } @@ -331,9 +331,14 @@ func RestoreEventActions(actions []dataprovider.BaseEventAction, inputFile strin } // RestoreEventRules restores the specified event rules -func RestoreEventRules(rules []dataprovider.EventRule, inputFile string, mode int, executor, ipAddress, role string) error { +func RestoreEventRules(rules []dataprovider.EventRule, inputFile string, mode int, executor, ipAddress, + role string, dumpVersion int, +) error { for _, rule := range rules { rule := rule // pin + if dumpVersion < 15 { + rule.Status = 1 + } r, err := dataprovider.EventRuleExists(rule.Name) if err == nil { if mode == 1 { diff --git a/internal/httpd/httpd_test.go b/internal/httpd/httpd_test.go index 07312351..64ec9dc7 100644 --- a/internal/httpd/httpd_test.go +++ b/internal/httpd/httpd_test.go @@ -1408,6 +1408,7 @@ func TestBasicActionRulesHandling(t *testing.T) { r := dataprovider.EventRule{ Name: "test rule name", + Status: 1, Description: "", Trigger: dataprovider.EventTriggerFsEvent, Conditions: dataprovider.EventConditions{ @@ -1629,6 +1630,7 @@ func TestActionRuleRelations(t *testing.T) { Order: 10, }, } + rule2.Status = 1 rule2, _, err = httpdtest.UpdateEventRule(r2, http.StatusOK) assert.NoError(t, err) if assert.Len(t, rule2.Actions, 1) { @@ -1936,6 +1938,11 @@ func TestEventRuleValidation(t *testing.T) { assert.NoError(t, err) assert.Contains(t, string(resp), "name is mandatory") rule.Name = "r" + rule.Status = 100 + _, resp, err = httpdtest.AddEventRule(rule, http.StatusBadRequest) + assert.NoError(t, err) + assert.Contains(t, string(resp), "invalid event rule status") + rule.Status = 1 rule.Trigger = 1000 _, resp, err = httpdtest.AddEventRule(rule, http.StatusBadRequest) assert.NoError(t, err) @@ -6522,7 +6529,9 @@ func TestProviderErrors(t *testing.T) { assert.NoError(t, err) user = getTestUser() user.ID = 1 - backupData := dataprovider.BackupData{} + backupData := dataprovider.BackupData{ + Version: dataprovider.DumpVersion, + } backupData.Users = append(backupData.Users, user) backupContent, err := json.Marshal(backupData) assert.NoError(t, err) @@ -6591,6 +6600,7 @@ func TestProviderErrors(t *testing.T) { Type: dataprovider.ActionTypeFolderQuotaReset, }, }, + Version: dataprovider.DumpVersion, } backupContent, err = json.Marshal(backupData) assert.NoError(t, err) @@ -6623,6 +6633,7 @@ func TestProviderErrors(t *testing.T) { }, }, }, + Version: dataprovider.DumpVersion, } backupContent, err = json.Marshal(backupData) assert.NoError(t, err) @@ -6636,6 +6647,7 @@ func TestProviderErrors(t *testing.T) { Name: "role1", }, }, + Version: dataprovider.DumpVersion, } backupContent, err = json.Marshal(backupData) assert.NoError(t, err) @@ -7036,7 +7048,9 @@ func TestRestoreShares(t *testing.T) { UsedTokens: 8, AllowFrom: []string{"127.0.0.0/8"}, } - backupData := dataprovider.BackupData{} + backupData := dataprovider.BackupData{ + Version: dataprovider.DumpVersion, + } backupData.Shares = append(backupData.Shares, share) backupContent, err := json.Marshal(backupData) assert.NoError(t, err) @@ -7088,7 +7102,9 @@ func TestLoaddataFromPostBody(t *testing.T) { admin.Permissions = []string{dataprovider.PermAdminAddUsers, dataprovider.PermAdminChangeUsers, dataprovider.PermAdminDeleteUsers, dataprovider.PermAdminViewUsers} admin.Role = role.Name - backupData := dataprovider.BackupData{} + backupData := dataprovider.BackupData{ + Version: dataprovider.DumpVersion, + } backupData.Users = append(backupData.Users, user) backupData.Groups = append(backupData.Groups, group) backupData.Admins = append(backupData.Admins, admin) @@ -7273,7 +7289,9 @@ func TestLoaddata(t *testing.T) { }, }, } - backupData := dataprovider.BackupData{} + backupData := dataprovider.BackupData{ + Version: 14, + } backupData.Users = append(backupData.Users, user) backupData.Roles = append(backupData.Roles, role) backupData.Groups = append(backupData.Groups, group) @@ -7350,6 +7368,7 @@ func TestLoaddata(t *testing.T) { rule, _, err = httpdtest.GetEventRuleByName(rule.Name, http.StatusOK) assert.NoError(t, err) + assert.Equal(t, 1, rule.Status) if assert.Len(t, rule.Actions, 1) { if assert.NotNil(t, rule.Actions[0].BaseEventAction.Options.HTTPConfig.Password) { assert.Equal(t, sdkkms.SecretStatusSecretBox, rule.Actions[0].BaseEventAction.Options.HTTPConfig.Password.GetStatus()) @@ -7516,7 +7535,9 @@ func TestLoaddataMode(t *testing.T) { }, }, } - backupData := dataprovider.BackupData{} + backupData := dataprovider.BackupData{ + Version: dataprovider.DumpVersion, + } backupData.Users = append(backupData.Users, user) backupData.Groups = append(backupData.Groups, group) backupData.Admins = append(backupData.Admins, admin) @@ -7603,6 +7624,7 @@ func TestLoaddataMode(t *testing.T) { rule, _, err = httpdtest.GetEventRuleByName(rule.Name, http.StatusOK) assert.NoError(t, err) + assert.Equal(t, 0, rule.Status) oldRuleDesc := rule.Description rule.Description = "new rule description" rule, _, err = httpdtest.UpdateEventRule(rule, http.StatusOK) @@ -17653,7 +17675,9 @@ func TestWebMaintenanceMock(t *testing.T) { Key: fmt.Sprintf("%v.%v", util.GenerateUniqueID(), util.GenerateUniqueID()), Scope: dataprovider.APIKeyScopeAdmin, } - backupData := dataprovider.BackupData{} + backupData := dataprovider.BackupData{ + Version: dataprovider.DumpVersion, + } backupData.Users = append(backupData.Users, user) backupData.Admins = append(backupData.Admins, admin) backupData.APIKeys = append(backupData.APIKeys, apiKey) @@ -19027,7 +19051,9 @@ func TestFolderTemplateMock(t *testing.T) { rr = executeRequest(req) checkResponseCode(t, http.StatusOK, rr) - dump = dataprovider.BackupData{} + dump = dataprovider.BackupData{ + Version: dataprovider.DumpVersion, + } err = json.Unmarshal(rr.Body.Bytes(), &dump) require.NoError(t, err) require.Len(t, dump.Users, 0) @@ -20530,6 +20556,7 @@ func TestWebEventRule(t *testing.T) { assert.NoError(t, err) rule := dataprovider.EventRule{ Name: "test_web_rule", + Status: 1, Description: "rule added using web API", Trigger: dataprovider.EventTriggerSchedule, Conditions: dataprovider.EventConditions{ @@ -20574,13 +20601,22 @@ func TestWebEventRule(t *testing.T) { form := make(url.Values) form.Set("name", rule.Name) form.Set("description", rule.Description) - form.Set("trigger", "a") + form.Set("status", "a") req, err := http.NewRequest(http.MethodPost, webAdminEventRulePath, bytes.NewBuffer([]byte(form.Encode()))) assert.NoError(t, err) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") setJWTCookieForReq(req, webToken) rr := executeRequest(req) checkResponseCode(t, http.StatusOK, rr) + assert.Contains(t, rr.Body.String(), "invalid status") + form.Set("status", fmt.Sprintf("%d", rule.Status)) + form.Set("trigger", "a") + req, err = http.NewRequest(http.MethodPost, webAdminEventRulePath, bytes.NewBuffer([]byte(form.Encode()))) + assert.NoError(t, err) + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + setJWTCookieForReq(req, webToken) + rr = executeRequest(req) + checkResponseCode(t, http.StatusOK, rr) assert.Contains(t, rr.Body.String(), "invalid trigger") form.Set("trigger", fmt.Sprintf("%d", rule.Trigger)) form.Set("schedule_hour0", rule.Conditions.Schedules[0].Hours) @@ -20668,13 +20704,15 @@ func TestWebEventRule(t *testing.T) { ruleGet, _, err := httpdtest.GetEventRuleByName(rule.Name, http.StatusOK) assert.NoError(t, err) assert.Equal(t, rule.Trigger, ruleGet.Trigger) + assert.Equal(t, rule.Status, ruleGet.Status) assert.Equal(t, rule.Description, ruleGet.Description) assert.Equal(t, rule.Conditions, ruleGet.Conditions) if assert.Len(t, ruleGet.Actions, 1) { assert.Equal(t, rule.Actions[0].Name, ruleGet.Actions[0].Name) assert.Equal(t, rule.Actions[0].Order, ruleGet.Actions[0].Order) } - // change rule trigger + // change rule trigger and status + rule.Status = 0 rule.Trigger = dataprovider.EventTriggerFsEvent rule.Conditions = dataprovider.EventConditions{ FsEvents: []string{"upload", "download"}, @@ -20707,6 +20745,7 @@ func TestWebEventRule(t *testing.T) { MaxFileSize: 5 * 1024 * 1024, }, } + form.Set("status", fmt.Sprintf("%d", rule.Status)) form.Set("trigger", fmt.Sprintf("%d", rule.Trigger)) for _, event := range rule.Conditions.FsEvents { form.Add("fs_events", event) @@ -20727,6 +20766,7 @@ func TestWebEventRule(t *testing.T) { // check the rule ruleGet, _, err = httpdtest.GetEventRuleByName(rule.Name, http.StatusOK) assert.NoError(t, err) + assert.Equal(t, rule.Status, ruleGet.Status) assert.Equal(t, rule.Trigger, ruleGet.Trigger) assert.Equal(t, rule.Description, ruleGet.Description) assert.Equal(t, rule.Conditions, ruleGet.Conditions) diff --git a/internal/httpd/webadmin.go b/internal/httpd/webadmin.go index 02df9bd7..3d6eb00a 100644 --- a/internal/httpd/webadmin.go +++ b/internal/httpd/webadmin.go @@ -2339,6 +2339,10 @@ func getEventRuleFromPostFields(r *http.Request) (dataprovider.EventRule, error) if err != nil { return dataprovider.EventRule{}, err } + status, err := strconv.Atoi(r.Form.Get("status")) + if err != nil { + return dataprovider.EventRule{}, fmt.Errorf("invalid status: %w", err) + } trigger, err := strconv.Atoi(r.Form.Get("trigger")) if err != nil { return dataprovider.EventRule{}, fmt.Errorf("invalid trigger: %w", err) @@ -2353,6 +2357,7 @@ func getEventRuleFromPostFields(r *http.Request) (dataprovider.EventRule, error) } rule := dataprovider.EventRule{ Name: r.Form.Get("name"), + Status: status, Description: r.Form.Get("description"), Trigger: trigger, Conditions: conditions, @@ -3499,6 +3504,7 @@ func (s *httpdServer) handleWebGetEventRules(w http.ResponseWriter, r *http.Requ func (s *httpdServer) handleWebAddEventRuleGet(w http.ResponseWriter, r *http.Request) { r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize) rule := dataprovider.EventRule{ + Status: 1, Trigger: dataprovider.EventTriggerFsEvent, } s.renderEventRulePage(w, r, rule, genericPageModeAdd, "") diff --git a/internal/httpdtest/httpdtest.go b/internal/httpdtest/httpdtest.go index f1805d3e..03cc1695 100644 --- a/internal/httpdtest/httpdtest.go +++ b/internal/httpdtest/httpdtest.go @@ -1601,6 +1601,9 @@ func checkEventRule(expected, actual dataprovider.EventRule) error { if dataprovider.ConvertName(expected.Name) != actual.Name { return errors.New("name mismatch") } + if expected.Status != actual.Status { + return errors.New("status mismatch") + } if expected.Description != actual.Description { return errors.New("description mismatch") } diff --git a/internal/service/service.go b/internal/service/service.go index cb1707a4..b1786853 100644 --- a/internal/service/service.go +++ b/internal/service/service.go @@ -382,7 +382,8 @@ func (s *Service) restoreDump(dump *dataprovider.BackupData) error { if err != nil { return fmt.Errorf("unable to restore event actions from file %#v: %v", s.LoadDataFrom, err) } - err = httpd.RestoreEventRules(dump.EventRules, s.LoadDataFrom, s.LoadDataMode, dataprovider.ActionExecutorSystem, "", "") + err = httpd.RestoreEventRules(dump.EventRules, s.LoadDataFrom, s.LoadDataMode, dataprovider.ActionExecutorSystem, + "", "", dump.Version) if err != nil { return fmt.Errorf("unable to restore event rules from file %#v: %v", s.LoadDataFrom, err) } diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml index 2dcb55ec..ccf7468d 100644 --- a/openapi/openapi.yaml +++ b/openapi/openapi.yaml @@ -6748,6 +6748,15 @@ components: name: type: string description: unique name + status: + type: integer + enum: + - 0 + - 1 + description: | + status: + * `0` disabled + * `1` enabled description: type: string description: optional description diff --git a/templates/webadmin/admin.html b/templates/webadmin/admin.html index 126fdff1..cff24d1d 100644 --- a/templates/webadmin/admin.html +++ b/templates/webadmin/admin.html @@ -41,6 +41,16 @@ along with this program. If not, see . +
+ +
+ +
+
+
@@ -60,16 +70,6 @@ along with this program. If not, see .
-
- -
- -
-
-
diff --git a/templates/webadmin/eventrule.html b/templates/webadmin/eventrule.html index a2370744..c8529083 100644 --- a/templates/webadmin/eventrule.html +++ b/templates/webadmin/eventrule.html @@ -41,6 +41,16 @@ along with this program. If not, see .
+
+ +
+ +
+
+
diff --git a/templates/webadmin/eventrules.html b/templates/webadmin/eventrules.html index c1d53087..2e1d1acc 100644 --- a/templates/webadmin/eventrules.html +++ b/templates/webadmin/eventrules.html @@ -39,6 +39,7 @@ along with this program. If not, see . Name + Status Description Trigger Actions @@ -48,6 +49,7 @@ along with this program. If not, see . {{range .Rules}} {{.Name}} + {{if eq .Status 1 }}Active{{else}}Inactive{{end}} {{.Description}} {{.GetTriggerAsString}} {{.GetActionsAsString}} @@ -184,11 +186,15 @@ along with this program. If not, see . ], "columnDefs": [ { - "targets": [0], + "targets": [0,1], "className": "noVis" }, { - "targets": [3], + "targets": [2], + "visible": false + }, + { + "targets": [4], "render": $.fn.dataTable.render.ellipsis(100, true) }, ],