event manager: add IP blocked trigger
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
parent
d65c00728a
commit
194c3c13ac
12 changed files with 247 additions and 31 deletions
|
@ -198,7 +198,7 @@ We only provide the slim variant and so the optional `git` dependency is not ava
|
|||
|
||||
### `sftpgo:<suite>-slim`
|
||||
|
||||
These tags provide a slimmer image that does not include the optional `git`, `rsync` and `jq` dependencies.
|
||||
These tags provide a slimmer image that does not include `jq` and the optional `git` and `rsync` dependencies.
|
||||
|
||||
### `sftpgo:<suite>-plugins`
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
ARCH=`uname -m`
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@ The following trigger events are supported:
|
|||
- `Filesystem events`, for example `upload`, `download` etc.
|
||||
- `Provider events`, for example `add`, `update`, `delete` user or other resources.
|
||||
- `Schedules`.
|
||||
- `IP Blocked`, this event can be generated if you enable the [defender](./defender.md).
|
||||
|
||||
You can further restrict a rule by specifying additional conditions that must be met before the rule’s actions are taken. For example you can react to uploads only if they are performed by a particular user or using a specified protocol.
|
||||
|
||||
|
@ -58,3 +59,4 @@ Some actions are not supported for some triggers, rules containing incompatible
|
|||
- `Filesystem events`, folder quota reset cannot be executed, we don't have a direct way to get the affected folder.
|
||||
- `Provider events`, user quota reset, transfer quota reset, data retention check and filesystem actions can be executed only if we modify a user. They will be executed for the affected user. Folder quota reset can be executed only for folders. Filesystem actions are not executed for `delete` user events because the actions is executed after the user deletion.
|
||||
- `Schedules`, filesystem actions cannot be executed, they require a user.
|
||||
- `IP Blocked`, user quota reset, folder quota reset, transfer quota reset, data retention check and filesystem actions cannot be executed, we only have an IP.
|
||||
|
|
8
go.mod
8
go.mod
|
@ -3,7 +3,7 @@ module github.com/drakkan/sftpgo/v2
|
|||
go 1.19
|
||||
|
||||
require (
|
||||
cloud.google.com/go/storage v1.24.0
|
||||
cloud.google.com/go/storage v1.25.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.2
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.4.1
|
||||
github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962
|
||||
|
@ -66,9 +66,9 @@ require (
|
|||
go.uber.org/automaxprocs v1.5.1
|
||||
gocloud.dev v0.26.0
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa
|
||||
golang.org/x/net v0.0.0-20220805013720-a33c5aa5df48
|
||||
golang.org/x/net v0.0.0-20220809184613-07c6da5e1ced
|
||||
golang.org/x/oauth2 v0.0.0-20220808172628-8227340efae7
|
||||
golang.org/x/sys v0.0.0-20220808155132-1c4a2a72c664
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab
|
||||
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9
|
||||
google.golang.org/api v0.92.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||
|
@ -168,5 +168,5 @@ replace (
|
|||
github.com/jlaffaye/ftp => github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9
|
||||
github.com/pkg/sftp => github.com/drakkan/sftp v0.0.0-20220716075551-51a5aa4e044d
|
||||
golang.org/x/crypto => github.com/drakkan/crypto v0.0.0-20220723143649-81550382d55e
|
||||
golang.org/x/net => github.com/drakkan/net v0.0.0-20220805164234-d1ea7e8d1b71
|
||||
golang.org/x/net => github.com/drakkan/net v0.0.0-20220811173512-bde04f9047cc
|
||||
)
|
||||
|
|
12
go.sum
12
go.sum
|
@ -77,8 +77,8 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f
|
|||
cloud.google.com/go/storage v1.21.0/go.mod h1:XmRlxkgPjlBONznT2dDUU/5XlpU2OjMnKuqnZI01LAA=
|
||||
cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y=
|
||||
cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc=
|
||||
cloud.google.com/go/storage v1.24.0 h1:a4N0gIkx83uoVFGz8B2eAV3OhN90QoWF5OZWLKl39ig=
|
||||
cloud.google.com/go/storage v1.24.0/go.mod h1:3xrJEFMXBsQLgxwThyjuD3aYlroL0TMRec1ypGUQ0KE=
|
||||
cloud.google.com/go/storage v1.25.0 h1:D2Dn0PslpK7Z3B2AvuUHyIC762bDbGJdlmQlCBR71os=
|
||||
cloud.google.com/go/storage v1.25.0/go.mod h1:Qys4JU+jeup3QnuKKAosWuxrD95C4MSqxfVDnSirDsI=
|
||||
cloud.google.com/go/trace v1.0.0/go.mod h1:4iErSByzxkyHWzzlAj63/Gmjz0NH1ASqhJguHpGcr6A=
|
||||
cloud.google.com/go/trace v1.2.0/go.mod h1:Wc8y/uYyOhPy12KEnXG9XGrvfMz5F5SrYecQlbW1rwM=
|
||||
contrib.go.opencensus.io/exporter/aws v0.0.0-20200617204711-c478e41e60e9/go.mod h1:uu1P0UCM/6RbsMrgPa98ll8ZcHM858i/AD06a9aLRCA=
|
||||
|
@ -266,8 +266,8 @@ github.com/drakkan/crypto v0.0.0-20220723143649-81550382d55e h1:ZvOJ5DqEUZig5lGl
|
|||
github.com/drakkan/crypto v0.0.0-20220723143649-81550382d55e/go.mod h1:SiM6ypd8Xu1xldObYtbDztuUU7xUzMnUULfphXFZmro=
|
||||
github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9 h1:LPH1dEblAOO/LoG7yHPMtBLXhQmjaga91/DDjWk9jWA=
|
||||
github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9/go.mod h1:2lmrmq866uF2tnje75wQHzmPXhmSWUt7Gyx2vgK1RCU=
|
||||
github.com/drakkan/net v0.0.0-20220805164234-d1ea7e8d1b71 h1:zC5D08STgdsK74Nh0457cZp7hKmbW+upbr8lfPo3CJw=
|
||||
github.com/drakkan/net v0.0.0-20220805164234-d1ea7e8d1b71/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
github.com/drakkan/net v0.0.0-20220811173512-bde04f9047cc h1:nWhdNJ31a4S7oBCwIRRPY/QfpOdHl3i3irjrJXrfM7w=
|
||||
github.com/drakkan/net v0.0.0-20220811173512-bde04f9047cc/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
github.com/drakkan/sftp v0.0.0-20220716075551-51a5aa4e044d h1:kNk/KRhszPJASp7WvjagNW254aKK643Lu8/fr4/ukiM=
|
||||
github.com/drakkan/sftp v0.0.0-20220716075551-51a5aa4e044d/go.mod h1:wHDZ0IZX6JcBYRK1TH9bcVq8G7TLpVHYIGJRFnmPfxg=
|
||||
github.com/eikenb/pipeat v0.0.0-20210730190139-06b3e6902001 h1:/ZshrfQzayqRSBDodmp3rhNCHJCff+utvgBuWRbiqu4=
|
||||
|
@ -974,8 +974,8 @@ golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220808155132-1c4a2a72c664 h1:v1W7bwXHsnLLloWYTVEdvGvA7BHMeBYsPcF0GLDxIRs=
|
||||
golang.org/x/sys v0.0.0-20220808155132-1c4a2a72c664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
|
|
|
@ -46,7 +46,7 @@ renewed by the SFTPGo service
|
|||
configDir = util.CleanDirInput(configDir)
|
||||
err := config.LoadConfig(configDir, configFile)
|
||||
if err != nil {
|
||||
logger.ErrorToConsole("Unable to initialize data provider, config load error: %v", err)
|
||||
logger.ErrorToConsole("Unable to initialize ACME, config load error: %v", err)
|
||||
return
|
||||
}
|
||||
acmeConfig := config.GetACMEConfig()
|
||||
|
|
|
@ -107,6 +107,13 @@ func (d *dbDefender) AddEvent(ip string, event HostEvent) {
|
|||
if host.Score > d.config.Threshold {
|
||||
banTime := time.Now().Add(time.Duration(d.config.BanTime) * time.Minute)
|
||||
err = dataprovider.SetDefenderBanTime(ip, util.GetTimeAsMsSinceEpoch(banTime))
|
||||
if err == nil {
|
||||
eventManager.handleIPBlockedEvent(EventParams{
|
||||
Event: ipBlockedEventName,
|
||||
IP: ip,
|
||||
Timestamp: time.Now().UnixNano(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
|
|
|
@ -209,6 +209,11 @@ func (d *memoryDefender) AddEvent(ip string, event HostEvent) {
|
|||
d.banned[ip] = time.Now().Add(time.Duration(d.config.BanTime) * time.Minute)
|
||||
delete(d.hosts, ip)
|
||||
d.cleanupBanned()
|
||||
eventManager.handleIPBlockedEvent(EventParams{
|
||||
Event: ipBlockedEventName,
|
||||
IP: ip,
|
||||
Timestamp: time.Now().UnixNano(),
|
||||
})
|
||||
} else {
|
||||
d.hosts[ip] = hs
|
||||
}
|
||||
|
|
|
@ -41,6 +41,10 @@ import (
|
|||
"github.com/drakkan/sftpgo/v2/internal/vfs"
|
||||
)
|
||||
|
||||
const (
|
||||
ipBlockedEventName = "IP Blocked"
|
||||
)
|
||||
|
||||
var (
|
||||
// eventManager handle the supported event rules actions
|
||||
eventManager eventRulesContainer
|
||||
|
@ -71,11 +75,12 @@ func init() {
|
|||
// eventRulesContainer stores event rules by trigger
|
||||
type eventRulesContainer struct {
|
||||
sync.RWMutex
|
||||
lastLoad int64
|
||||
FsEvents []dataprovider.EventRule
|
||||
ProviderEvents []dataprovider.EventRule
|
||||
Schedules []dataprovider.EventRule
|
||||
IPBlockedEvents []dataprovider.EventRule
|
||||
schedulesMapping map[string][]cron.EntryID
|
||||
lastLoad int64
|
||||
concurrencyGuard chan struct{}
|
||||
}
|
||||
|
||||
|
@ -124,6 +129,15 @@ func (r *eventRulesContainer) removeRuleInternal(name string) {
|
|||
return
|
||||
}
|
||||
}
|
||||
for idx := range r.IPBlockedEvents {
|
||||
if r.IPBlockedEvents[idx].Name == name {
|
||||
lastIdx := len(r.IPBlockedEvents) - 1
|
||||
r.IPBlockedEvents[idx] = r.IPBlockedEvents[lastIdx]
|
||||
r.IPBlockedEvents = r.IPBlockedEvents[:lastIdx]
|
||||
eventManagerLog(logger.LevelDebug, "removed rule %q from IP blocked events", name)
|
||||
return
|
||||
}
|
||||
}
|
||||
for idx := range r.Schedules {
|
||||
if r.Schedules[idx].Name == name {
|
||||
if schedules, ok := r.schedulesMapping[name]; ok {
|
||||
|
@ -160,6 +174,9 @@ func (r *eventRulesContainer) addUpdateRuleInternal(rule dataprovider.EventRule)
|
|||
case dataprovider.EventTriggerProviderEvent:
|
||||
r.ProviderEvents = append(r.ProviderEvents, rule)
|
||||
eventManagerLog(logger.LevelDebug, "added rule %q to provider events", rule.Name)
|
||||
case dataprovider.EventTriggerIPBlocked:
|
||||
r.IPBlockedEvents = append(r.IPBlockedEvents, rule)
|
||||
eventManagerLog(logger.LevelDebug, "added rule %q to IP blocked events", rule.Name)
|
||||
case dataprovider.EventTriggerSchedule:
|
||||
for _, schedule := range rule.Conditions.Schedules {
|
||||
cronSpec := schedule.GetCronSpec()
|
||||
|
@ -200,8 +217,8 @@ func (r *eventRulesContainer) loadRules() {
|
|||
r.addUpdateRuleInternal(rule)
|
||||
}
|
||||
}
|
||||
eventManagerLog(logger.LevelDebug, "event rules updated, fs events: %d, provider events: %d, schedules: %d",
|
||||
len(r.FsEvents), len(r.ProviderEvents), len(r.Schedules))
|
||||
eventManagerLog(logger.LevelDebug, "event rules updated, fs events: %d, provider events: %d, schedules: %d, ip blocked events: %d",
|
||||
len(r.FsEvents), len(r.ProviderEvents), len(r.Schedules), len(r.IPBlockedEvents))
|
||||
|
||||
r.setLastLoadTime(modTime)
|
||||
}
|
||||
|
@ -323,6 +340,28 @@ func (r *eventRulesContainer) handleProviderEvent(params EventParams) {
|
|||
}
|
||||
}
|
||||
|
||||
func (r *eventRulesContainer) handleIPBlockedEvent(params EventParams) {
|
||||
r.RLock()
|
||||
defer r.RUnlock()
|
||||
|
||||
if len(r.IPBlockedEvents) == 0 {
|
||||
return
|
||||
}
|
||||
var rules []dataprovider.EventRule
|
||||
for _, rule := range r.IPBlockedEvents {
|
||||
if err := rule.CheckActionsConsistency(""); err == nil {
|
||||
rules = append(rules, rule)
|
||||
} else {
|
||||
eventManagerLog(logger.LevelWarn, "rule %q skipped: %v, event %q",
|
||||
rule.Name, err, params.Event)
|
||||
}
|
||||
}
|
||||
|
||||
if len(rules) > 0 {
|
||||
go executeAsyncRulesActions(rules, params)
|
||||
}
|
||||
}
|
||||
|
||||
// EventParams defines the supported event parameters
|
||||
type EventParams struct {
|
||||
Name string
|
||||
|
|
|
@ -3530,6 +3530,129 @@ func TestEventRuleFsActions(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestEventRuleIPBlocked(t *testing.T) {
|
||||
oldConfig := config.GetCommonConfig()
|
||||
|
||||
cfg := config.GetCommonConfig()
|
||||
cfg.DefenderConfig.Enabled = true
|
||||
cfg.DefenderConfig.Threshold = 3
|
||||
cfg.DefenderConfig.ScoreLimitExceeded = 2
|
||||
|
||||
err := common.Initialize(cfg, 0)
|
||||
assert.NoError(t, err)
|
||||
|
||||
smtpCfg := smtp.Config{
|
||||
Host: "127.0.0.1",
|
||||
Port: 2525,
|
||||
From: "notification@example.com",
|
||||
TemplatesPath: "templates",
|
||||
}
|
||||
err = smtpCfg.Initialize(configDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
a1 := dataprovider.BaseEventAction{
|
||||
Name: "action1",
|
||||
Type: dataprovider.ActionTypeEmail,
|
||||
Options: dataprovider.BaseEventActionOptions{
|
||||
EmailConfig: dataprovider.EventActionEmailConfig{
|
||||
Recipients: []string{"test3@example.com", "test4@example.com"},
|
||||
Subject: `New "{{Event}}"`,
|
||||
Body: "IP: {{IP}} Timestamp: {{Timestamp}}",
|
||||
},
|
||||
},
|
||||
}
|
||||
action1, _, err := httpdtest.AddEventAction(a1, http.StatusCreated)
|
||||
assert.NoError(t, err)
|
||||
|
||||
a2 := dataprovider.BaseEventAction{
|
||||
Name: "action2",
|
||||
Type: dataprovider.ActionTypeFolderQuotaReset,
|
||||
}
|
||||
action2, _, err := httpdtest.AddEventAction(a2, http.StatusCreated)
|
||||
assert.NoError(t, err)
|
||||
|
||||
r1 := dataprovider.EventRule{
|
||||
Name: "test rule ip blocked",
|
||||
Trigger: dataprovider.EventTriggerIPBlocked,
|
||||
Actions: []dataprovider.EventAction{
|
||||
{
|
||||
BaseEventAction: dataprovider.BaseEventAction{
|
||||
Name: action1.Name,
|
||||
},
|
||||
Order: 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
rule1, _, err := httpdtest.AddEventRule(r1, http.StatusCreated)
|
||||
assert.NoError(t, err)
|
||||
r2 := dataprovider.EventRule{
|
||||
Name: "test rule 2",
|
||||
Trigger: dataprovider.EventTriggerIPBlocked,
|
||||
Actions: []dataprovider.EventAction{
|
||||
{
|
||||
BaseEventAction: dataprovider.BaseEventAction{
|
||||
Name: action1.Name,
|
||||
},
|
||||
Order: 1,
|
||||
},
|
||||
{
|
||||
BaseEventAction: dataprovider.BaseEventAction{
|
||||
Name: action2.Name,
|
||||
},
|
||||
Order: 2,
|
||||
},
|
||||
},
|
||||
}
|
||||
rule2, _, err := httpdtest.AddEventRule(r2, http.StatusCreated)
|
||||
assert.NoError(t, err)
|
||||
|
||||
u := getTestUser()
|
||||
user, _, err := httpdtest.AddUser(u, http.StatusCreated)
|
||||
assert.NoError(t, err)
|
||||
lastReceivedEmail.reset()
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
assert.Empty(t, lastReceivedEmail.get().From, string(lastReceivedEmail.get().Data))
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
user.Password = "wrong_pwd"
|
||||
_, _, err = getSftpClient(user)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
// the client is now banned
|
||||
user.Password = defaultPassword
|
||||
_, _, err = getSftpClient(user)
|
||||
assert.Error(t, err)
|
||||
// check the email notification
|
||||
assert.Eventually(t, func() bool {
|
||||
return lastReceivedEmail.get().From != ""
|
||||
}, 3000*time.Millisecond, 100*time.Millisecond)
|
||||
email := lastReceivedEmail.get()
|
||||
assert.Len(t, email.To, 2)
|
||||
assert.True(t, util.Contains(email.To, "test3@example.com"))
|
||||
assert.True(t, util.Contains(email.To, "test4@example.com"))
|
||||
assert.Contains(t, string(email.Data), `Subject: New "IP Blocked"`)
|
||||
|
||||
err = dataprovider.DeleteEventRule(rule1.Name, "", "")
|
||||
assert.NoError(t, err)
|
||||
err = dataprovider.DeleteEventRule(rule2.Name, "", "")
|
||||
assert.NoError(t, err)
|
||||
err = dataprovider.DeleteEventAction(action1.Name, "", "")
|
||||
assert.NoError(t, err)
|
||||
err = dataprovider.DeleteEventAction(action2.Name, "", "")
|
||||
assert.NoError(t, err)
|
||||
err = dataprovider.DeleteUser(user.Username, "", "")
|
||||
assert.NoError(t, err)
|
||||
err = os.RemoveAll(user.GetHomeDir())
|
||||
assert.NoError(t, err)
|
||||
|
||||
smtpCfg = smtp.Config{}
|
||||
err = smtpCfg.Initialize(configDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = common.Initialize(oldConfig, 0)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestSyncUploadAction(t *testing.T) {
|
||||
if runtime.GOOS == osWindows {
|
||||
t.Skip("this test is not available on Windows")
|
||||
|
|
|
@ -84,10 +84,12 @@ const (
|
|||
// Provider events such as add, update, delete
|
||||
EventTriggerProviderEvent
|
||||
EventTriggerSchedule
|
||||
EventTriggerIPBlocked
|
||||
)
|
||||
|
||||
var (
|
||||
supportedEventTriggers = []int{EventTriggerFsEvent, EventTriggerProviderEvent, EventTriggerSchedule}
|
||||
supportedEventTriggers = []int{EventTriggerFsEvent, EventTriggerProviderEvent, EventTriggerSchedule,
|
||||
EventTriggerIPBlocked}
|
||||
)
|
||||
|
||||
func isEventTriggerValid(trigger int) bool {
|
||||
|
@ -100,6 +102,8 @@ func getTriggerTypeAsString(trigger int) string {
|
|||
return "Filesystem event"
|
||||
case EventTriggerProviderEvent:
|
||||
return "Provider event"
|
||||
case EventTriggerIPBlocked:
|
||||
return "IP blocked"
|
||||
default:
|
||||
return "Schedule"
|
||||
}
|
||||
|
@ -881,6 +885,15 @@ func (c *EventConditions) validate(trigger int) error {
|
|||
return err
|
||||
}
|
||||
}
|
||||
case EventTriggerIPBlocked:
|
||||
c.FsEvents = nil
|
||||
c.ProviderEvents = nil
|
||||
c.Options.Names = nil
|
||||
c.Options.FsPaths = nil
|
||||
c.Options.Protocols = nil
|
||||
c.Options.MinFileSize = 0
|
||||
c.Options.MaxFileSize = 0
|
||||
c.Schedules = nil
|
||||
default:
|
||||
c.FsEvents = nil
|
||||
c.ProviderEvents = nil
|
||||
|
@ -1000,24 +1013,43 @@ func (r *EventRule) validate() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (r *EventRule) checkIPBlockedActions() error {
|
||||
unavailableActions := []int{ActionTypeUserQuotaReset, ActionTypeFolderQuotaReset, ActionTypeTransferQuotaReset,
|
||||
ActionTypeDataRetentionCheck, ActionTypeFilesystem}
|
||||
for _, action := range r.Actions {
|
||||
if util.Contains(unavailableActions, action.Type) {
|
||||
return fmt.Errorf("action %q, type %q is not supported for IP blocked events",
|
||||
action.Name, getActionTypeAsString(action.Type))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *EventRule) checkProviderEventActions(providerObjectType string) error {
|
||||
// user quota reset, transfer quota reset, data retention check and filesystem actions
|
||||
// can be executed only if we modify a user. They will be executed for the
|
||||
// affected user. Folder quota reset can be executed only for folders.
|
||||
userSpecificActions := []int{ActionTypeUserQuotaReset, ActionTypeTransferQuotaReset,
|
||||
ActionTypeDataRetentionCheck, ActionTypeFilesystem}
|
||||
for _, action := range r.Actions {
|
||||
if util.Contains(userSpecificActions, action.Type) && providerObjectType != actionObjectUser {
|
||||
return fmt.Errorf("action %q, type %q is only supported for provider user events",
|
||||
action.Name, getActionTypeAsString(action.Type))
|
||||
}
|
||||
if action.Type == ActionTypeFolderQuotaReset && providerObjectType != actionObjectFolder {
|
||||
return fmt.Errorf("action %q, type %q is only supported for provider folder events",
|
||||
action.Name, getActionTypeAsString(action.Type))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckActionsConsistency returns an error if the actions cannot be executed
|
||||
func (r *EventRule) CheckActionsConsistency(providerObjectType string) error {
|
||||
switch r.Trigger {
|
||||
case EventTriggerProviderEvent:
|
||||
// user quota reset, transfer quota reset, data retention check and filesystem actions
|
||||
// can be executed only if we modify a user. They will be executed for the
|
||||
// affected user. Folder quota reset can be executed only for folders.
|
||||
userSpecificActions := []int{ActionTypeUserQuotaReset, ActionTypeTransferQuotaReset,
|
||||
ActionTypeDataRetentionCheck, ActionTypeFilesystem}
|
||||
for _, action := range r.Actions {
|
||||
if util.Contains(userSpecificActions, action.Type) && providerObjectType != actionObjectUser {
|
||||
return fmt.Errorf("action %q, type %q is only supported for provider user events",
|
||||
action.Name, getActionTypeAsString(action.Type))
|
||||
}
|
||||
if action.Type == ActionTypeFolderQuotaReset && providerObjectType != actionObjectFolder {
|
||||
return fmt.Errorf("action %q, type %q is only supported for provider folder events",
|
||||
action.Name, getActionTypeAsString(action.Type))
|
||||
}
|
||||
if err := r.checkProviderEventActions(providerObjectType); err != nil {
|
||||
return err
|
||||
}
|
||||
case EventTriggerFsEvent:
|
||||
// folder quota reset cannot be executed
|
||||
|
@ -1035,6 +1067,10 @@ func (r *EventRule) CheckActionsConsistency(providerObjectType string) error {
|
|||
action.Name, getActionTypeAsString(action.Type))
|
||||
}
|
||||
}
|
||||
case EventTriggerIPBlocked:
|
||||
if err := r.checkIPBlockedActions(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -183,7 +183,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card bg-light mb-3">
|
||||
<div class="card bg-light mb-3 trigger trigger-fs trigger-provider trigger-schedule">
|
||||
<div class="card-header">
|
||||
<b>Name filters</b>
|
||||
</div>
|
||||
|
@ -543,6 +543,9 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
case 3:
|
||||
$('.trigger-schedule').show();
|
||||
break;
|
||||
case '4':
|
||||
case 4:
|
||||
break;
|
||||
default:
|
||||
console.log(`unsupported event trigger type: ${val}`);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue