diff --git a/docs/eventmanager.md b/docs/eventmanager.md index fc3c0028..ae2f2743 100644 --- a/docs/eventmanager.md +++ b/docs/eventmanager.md @@ -45,7 +45,7 @@ The following trigger events are supported: 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. -Actions such as user quota reset, transfer quota reset, data retention check and folder quota reset are executed for all matching users if the trigger is a schedule or for the affected user if the trigger is a provider event or a filesystem action. +Actions such as user quota reset, transfer quota reset, data retention check, folder quota reset and filesystem events are executed for all matching users if the trigger is a schedule or for the affected user if the trigger is a provider event or a filesystem action. Actions are executed in a sequential order except for sync actions that are executed before the others. For each action associated to a rule you can define the following settings: @@ -59,6 +59,5 @@ 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. - `Certificate`, user quota reset, folder quota reset, transfer quota reset, data retention check and filesystem actions cannot be executed. diff --git a/go.mod b/go.mod index 59bedd17..764cf50e 100644 --- a/go.mod +++ b/go.mod @@ -15,13 +15,13 @@ require ( github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.27 github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.13.12 github.com/aws/aws-sdk-go-v2/service/s3 v1.27.5 - github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.15.17 + github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.15.18 github.com/aws/aws-sdk-go-v2/service/sts v1.16.13 github.com/cockroachdb/cockroach-go/v2 v2.2.15 github.com/coreos/go-oidc/v3 v3.2.0 github.com/eikenb/pipeat v0.0.0-20210730190139-06b3e6902001 github.com/fclairamb/ftpserverlib v0.19.0 - github.com/fclairamb/go-log v0.4.0 + github.com/fclairamb/go-log v0.4.1 github.com/go-acme/lego/v4 v4.8.0 github.com/go-chi/chi/v5 v5.0.8-0.20220512131524-9e71a0d4b3d6 github.com/go-chi/jwtauth/v5 v5.0.2 @@ -32,7 +32,7 @@ require ( github.com/google/uuid v1.3.0 github.com/grandcat/zeroconf v1.0.0 github.com/hashicorp/go-hclog v1.2.2 - github.com/hashicorp/go-plugin v1.4.4 + github.com/hashicorp/go-plugin v1.4.5 github.com/hashicorp/go-retryablehttp v0.7.1 github.com/jlaffaye/ftp v0.0.0-20201112195030-9aae4d151126 github.com/klauspost/compress v1.15.9 @@ -68,15 +68,15 @@ require ( golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa golang.org/x/net v0.0.0-20220811182439-13a9a731de15 golang.org/x/oauth2 v0.0.0-20220808172628-8227340efae7 - golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab + golang.org/x/sys v0.0.0-20220818161305-2296e01440c6 golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 - google.golang.org/api v0.92.0 + google.golang.org/api v0.93.0 gopkg.in/natefinch/lumberjack.v2 v2.0.0 ) require ( cloud.google.com/go v0.103.0 // indirect - cloud.google.com/go/compute v1.8.0 // indirect + cloud.google.com/go/compute v1.9.0 // indirect cloud.google.com/go/iam v0.3.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 // indirect github.com/ajg/form v1.5.1 // indirect @@ -99,12 +99,12 @@ require ( github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect github.com/fatih/color v1.13.0 // indirect github.com/fsnotify/fsnotify v1.5.4 // indirect github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-test/deep v1.0.8 // indirect - github.com/goccy/go-json v0.9.10 // indirect + github.com/goccy/go-json v0.9.11 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/google/go-cmp v0.5.8 // indirect @@ -155,7 +155,7 @@ require ( golang.org/x/tools v0.1.12 // indirect golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959 // indirect + google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1 // indirect google.golang.org/grpc v1.48.0 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/go.sum b/go.sum index ecb497dd..f9e6392c 100644 --- a/go.sum +++ b/go.sum @@ -48,8 +48,8 @@ cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6m cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= -cloud.google.com/go/compute v1.8.0 h1:NLtR56/eKx9K1s2Tw/4hec2vsU1S3WeKRMj8HXbBo6E= -cloud.google.com/go/compute v1.8.0/go.mod h1:boQ44qJsMqZjKzzsEkoJWQGj4h8ygmyk17UArClWzmg= +cloud.google.com/go/compute v1.9.0 h1:ED/FP4xv8GJw63v556/ASNc1CeeLUO2Bs8nzaHchkHg= +cloud.google.com/go/compute v1.9.0/go.mod h1:lWv1h/zUWTm/LozzfTJhBSkd6ShQq8la8VeeuOEGxfY= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY= @@ -191,8 +191,8 @@ github.com/aws/aws-sdk-go-v2/service/s3 v1.26.3/go.mod h1:g1qvDuRsJY+XghsV6zg00Z github.com/aws/aws-sdk-go-v2/service/s3 v1.27.5 h1:h9qqTedYnA9JcWjKyLV6UYIMSdp91ExLCUbjbpDLH7A= github.com/aws/aws-sdk-go-v2/service/s3 v1.27.5/go.mod h1:J8SS5Tp/zeLxaubB0xGfKnVrvssNBNLwTipreTKLhjQ= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.15.4/go.mod h1:PJc8s+lxyU8rrre0/4a0pn2wgwiDvOEzoOjcJUBr67o= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.15.17 h1:x4JtJ0TaVVCoNc3bUtv0W5VvMLFiQ1++ReiRfSxRYf8= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.15.17/go.mod h1:HvF8QZUW+evBsd/SJn4VA0WWW5qVMKxPpWiRRK4w3eM= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.15.18 h1:OEPeoMWuUp1SvUvrLMh8B7SJPRz6M1hP/AV4pmXybx4= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.15.18/go.mod h1:HvF8QZUW+evBsd/SJn4VA0WWW5qVMKxPpWiRRK4w3eM= github.com/aws/aws-sdk-go-v2/service/sns v1.17.4/go.mod h1:kElt+uCcXxcqFyc+bQqZPFD9DME/eC6oHBXvFzQ9Bcw= github.com/aws/aws-sdk-go-v2/service/sqs v1.18.3/go.mod h1:skmQo0UPvsjsuYYSYMVmrPc1HWCbHUJyrCEp+ZaLzqM= github.com/aws/aws-sdk-go-v2/service/ssm v1.24.1/go.mod h1:NR/xoKjdbRJ+qx0pMR4mI+N/H1I1ynHwXnO6FowXJc0= @@ -254,8 +254,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d/go.mod h1:tmAIfUFEirG/Y8jhZ9M+h36obRZAk/1fcSpXwAVlfqE= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc= github.com/denisenkom/go-mssqldb v0.12.0/go.mod h1:iiK0YP1ZeepvmBQk/QpLEhhTNJgfzrpArPY/aFvc9yU= github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY= github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= @@ -286,8 +286,8 @@ github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fclairamb/ftpserverlib v0.19.0 h1:5QcSQ0OIJBlezIqmGehiL/AVsRb6dIkMxbkuhyPkESM= github.com/fclairamb/ftpserverlib v0.19.0/go.mod h1:pmukdVOFKKUY9zjWRoxFW8JAljyulC/uK5FfusJzK2E= -github.com/fclairamb/go-log v0.4.0 h1:HLm0yU9IzNCqayuTqtLyWUy/Bjud7+DZWTSg0lAC5pQ= -github.com/fclairamb/go-log v0.4.0/go.mod h1:sw1KvnkZ4wKCYkvy4SL3qVZcJSWFP8Ure4pM3z+KNn4= +github.com/fclairamb/go-log v0.4.1 h1:rLtdSG9x2pK41AIAnE8WYpl05xBJfw1ZyYxZaXFcBsM= +github.com/fclairamb/go-log v0.4.1/go.mod h1:sw1KvnkZ4wKCYkvy4SL3qVZcJSWFP8Ure4pM3z+KNn4= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= @@ -338,8 +338,8 @@ github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6Wezm github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= github.com/goccy/go-json v0.7.6/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/goccy/go-json v0.9.10 h1:hCeNmprSNLB8B8vQKWl6DpuH0t60oEs+TAk9a7CScKc= -github.com/goccy/go-json v0.9.10/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk= +github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= @@ -465,8 +465,8 @@ github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/S github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-hclog v1.2.2 h1:ihRI7YFwcZdiSD7SIenIhHfQH3OuDvWerAUBZbeQS3M= github.com/hashicorp/go-hclog v1.2.2/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= -github.com/hashicorp/go-plugin v1.4.4 h1:NVdrSdFRt3SkZtNckJ6tog7gbpRrcbOjQi/rgF7JYWQ= -github.com/hashicorp/go-plugin v1.4.4/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s= +github.com/hashicorp/go-plugin v1.4.5 h1:oTE/oQR4eghggRg8VY7PAz3dr++VwDNBGCcOfIvHpBo= +github.com/hashicorp/go-plugin v1.4.5/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s= github.com/hashicorp/go-retryablehttp v0.7.1 h1:sUiuQAnLlbvmExtFQs72iFW/HXeUn8Z1aJLQ4LJJbTQ= github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -976,8 +976,9 @@ 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-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220818161305-2296e01440c6 h1:Sx/u41w+OwrInGdEckYmEuU5gHoGSL4QbDz3S9s6j4U= +golang.org/x/sys v0.0.0-20220818161305-2296e01440c6/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= @@ -1120,8 +1121,8 @@ google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3 google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g= google.golang.org/api v0.86.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= -google.golang.org/api v0.92.0 h1:8JHk7q/+rJla+iRsWj9FQ9/wjv2M1SKtpKSdmLhxPT0= -google.golang.org/api v0.92.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= +google.golang.org/api v0.93.0 h1:T2xt9gi0gHdxdnRkVQhT8mIvPaXKNsDNWz+L696M66M= +google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1228,8 +1229,8 @@ google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljW google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959 h1:hw4Y42zL1VyVKxPgRHHh191fpVBGV8sNVmcow5Z8VXY= -google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1 h1:C2UVWqrgLYKrT5nh5oU6hLRm1AeEklCK5eloQA1NtFY= +google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= diff --git a/internal/common/eventmanager.go b/internal/common/eventmanager.go index ecf67bf4..39bba743 100644 --- a/internal/common/eventmanager.go +++ b/internal/common/eventmanager.go @@ -628,8 +628,8 @@ func executeEmailRuleAction(c dataprovider.EventActionEmailConfig, params EventP return err } -func getUserForEventAction(username string) (dataprovider.User, error) { - user, err := dataprovider.GetUserWithGroupSettings(username) +func getUserForEventAction(user dataprovider.User) (dataprovider.User, error) { + err := user.LoadAndApplyGroupSettings() if err != nil { return dataprovider.User{}, err } @@ -649,8 +649,8 @@ func executeDeleteFileFsAction(conn *BaseConnection, item string, info os.FileIn return conn.RemoveFile(fs, fsPath, item, info) } -func executeDeleteFsAction(deletes []string, replacer *strings.Replacer, username string) error { - user, err := getUserForEventAction(username) +func executeDeleteFsActionForUser(deletes []string, replacer *strings.Replacer, user dataprovider.User) error { + user, err := getUserForEventAction(user) if err != nil { return err } @@ -684,8 +684,40 @@ func executeDeleteFsAction(deletes []string, replacer *strings.Replacer, usernam return nil } -func executeMkDirsFsAction(dirs []string, replacer *strings.Replacer, username string) error { - user, err := getUserForEventAction(username) +func executeDeleteFsRuleAction(deletes []string, replacer *strings.Replacer, + conditions dataprovider.ConditionOptions, params EventParams, +) error { + users, err := params.getUsers() + if err != nil { + return fmt.Errorf("unable to get users: %w", err) + } + var failures []string + executed := 0 + for _, user := range users { + // if sender is set, the conditions have already been evaluated + if params.sender == "" && !checkEventConditionPatterns(user.Username, conditions.Names) { + eventManagerLog(logger.LevelDebug, "skipping fs delete for user %s, name conditions don't match", + user.Username) + continue + } + executed++ + if err = executeDeleteFsActionForUser(deletes, replacer, user); err != nil { + failures = append(failures, user.Username) + continue + } + } + if len(failures) > 0 { + return fmt.Errorf("fs delete failed for users: %+v", failures) + } + if executed == 0 { + eventManagerLog(logger.LevelError, "no delete executed") + return errors.New("no delete executed") + } + return nil +} + +func executeMkDirsFsActionForUser(dirs []string, replacer *strings.Replacer, user dataprovider.User) error { + user, err := getUserForEventAction(user) if err != nil { return err } @@ -709,8 +741,42 @@ func executeMkDirsFsAction(dirs []string, replacer *strings.Replacer, username s return nil } -func executeRenameFsAction(renames []dataprovider.KeyValue, replacer *strings.Replacer, username string) error { - user, err := getUserForEventAction(username) +func executeMkdirFsRuleAction(dirs []string, replacer *strings.Replacer, + conditions dataprovider.ConditionOptions, params EventParams, +) error { + users, err := params.getUsers() + if err != nil { + return fmt.Errorf("unable to get users: %w", err) + } + var failures []string + executed := 0 + for _, user := range users { + // if sender is set, the conditions have already been evaluated + if params.sender == "" && !checkEventConditionPatterns(user.Username, conditions.Names) { + eventManagerLog(logger.LevelDebug, "skipping fs mkdir for user %s, name conditions don't match", + user.Username) + continue + } + executed++ + if err = executeMkDirsFsActionForUser(dirs, replacer, user); err != nil { + failures = append(failures, user.Username) + continue + } + } + if len(failures) > 0 { + return fmt.Errorf("fs mkdir failed for users: %+v", failures) + } + if executed == 0 { + eventManagerLog(logger.LevelError, "no mkdir executed") + return errors.New("no mkdir executed") + } + return nil +} + +func executeRenameFsActionForUser(renames []dataprovider.KeyValue, replacer *strings.Replacer, + user dataprovider.User, +) error { + user, err := getUserForEventAction(user) if err != nil { return err } @@ -732,17 +798,51 @@ func executeRenameFsAction(renames []dataprovider.KeyValue, replacer *strings.Re return nil } -func executeFsRuleAction(c dataprovider.EventActionFilesystemConfig, params EventParams) error { +func executeRenameFsRuleAction(renames []dataprovider.KeyValue, replacer *strings.Replacer, + conditions dataprovider.ConditionOptions, params EventParams, +) error { + users, err := params.getUsers() + if err != nil { + return fmt.Errorf("unable to get users: %w", err) + } + var failures []string + executed := 0 + for _, user := range users { + // if sender is set, the conditions have already been evaluated + if params.sender == "" && !checkEventConditionPatterns(user.Username, conditions.Names) { + eventManagerLog(logger.LevelDebug, "skipping fs rename for user %s, name conditions don't match", + user.Username) + continue + } + executed++ + if err = executeRenameFsActionForUser(renames, replacer, user); err != nil { + failures = append(failures, user.Username) + continue + } + } + if len(failures) > 0 { + return fmt.Errorf("fs rename failed for users: %+v", failures) + } + if executed == 0 { + eventManagerLog(logger.LevelError, "no rename executed") + return errors.New("no rename executed") + } + return nil +} + +func executeFsRuleAction(c dataprovider.EventActionFilesystemConfig, conditions dataprovider.ConditionOptions, + params EventParams, +) error { addObjectData := false replacements := params.getStringReplacements(addObjectData) replacer := strings.NewReplacer(replacements...) switch c.Type { case dataprovider.FilesystemActionRename: - return executeRenameFsAction(c.Renames, replacer, params.sender) + return executeRenameFsRuleAction(c.Renames, replacer, conditions, params) case dataprovider.FilesystemActionDelete: - return executeDeleteFsAction(c.Deletes, replacer, params.sender) + return executeDeleteFsRuleAction(c.Deletes, replacer, conditions, params) case dataprovider.FilesystemActionMkdirs: - return executeMkDirsFsAction(c.MkDirs, replacer, params.sender) + return executeMkdirFsRuleAction(c.MkDirs, replacer, conditions, params) default: return fmt.Errorf("unsupported filesystem action %d", c.Type) } @@ -953,7 +1053,7 @@ func executeRuleAction(action dataprovider.BaseEventAction, params EventParams, case dataprovider.ActionTypeDataRetentionCheck: return executeDataRetentionCheckRuleAction(action.Options.RetentionConfig, conditions, params) case dataprovider.ActionTypeFilesystem: - return executeFsRuleAction(action.Options.FsConfig, params) + return executeFsRuleAction(action.Options.FsConfig, conditions, params) default: return fmt.Errorf("unsupported action type: %d", action.Type) } @@ -1070,10 +1170,6 @@ func (j *eventCronJob) Run() { eventManagerLog(logger.LevelError, "unable to load rule with name %q", j.ruleName) return } - if err = rule.CheckActionsConsistency(""); err != nil { - eventManagerLog(logger.LevelWarn, "scheduled rule %q skipped: %v", rule.Name, err) - return - } task, err := j.getTask(rule) if err != nil { return diff --git a/internal/common/eventmanager_test.go b/internal/common/eventmanager_test.go index d182960c..9eda71c7 100644 --- a/internal/common/eventmanager_test.go +++ b/internal/common/eventmanager_test.go @@ -275,10 +275,18 @@ func TestEventManagerErrors(t *testing.T) { assert.Error(t, err) err = executeTransferQuotaResetRuleAction(dataprovider.ConditionOptions{}, EventParams{}) assert.Error(t, err) + err = executeDeleteFsRuleAction(nil, nil, dataprovider.ConditionOptions{}, EventParams{}) + assert.Error(t, err) + err = executeMkdirFsRuleAction(nil, nil, dataprovider.ConditionOptions{}, EventParams{}) + assert.Error(t, err) + err = executeRenameFsRuleAction(nil, nil, dataprovider.ConditionOptions{}, EventParams{}) + assert.Error(t, err) + + groupName := "agroup" err = executeQuotaResetForUser(dataprovider.User{ Groups: []sdk.GroupMapping{ { - Name: "agroup", + Name: groupName, Type: sdk.GroupTypePrimary, }, }, @@ -287,12 +295,39 @@ func TestEventManagerErrors(t *testing.T) { err = executeDataRetentionCheckForUser(dataprovider.User{ Groups: []sdk.GroupMapping{ { - Name: "agroup", + Name: groupName, Type: sdk.GroupTypePrimary, }, }, }, nil) assert.Error(t, err) + err = executeDeleteFsActionForUser(nil, nil, dataprovider.User{ + Groups: []sdk.GroupMapping{ + { + Name: groupName, + Type: sdk.GroupTypePrimary, + }, + }, + }) + assert.Error(t, err) + err = executeMkDirsFsActionForUser(nil, nil, dataprovider.User{ + Groups: []sdk.GroupMapping{ + { + Name: groupName, + Type: sdk.GroupTypePrimary, + }, + }, + }) + assert.Error(t, err) + err = executeRenameFsActionForUser(nil, nil, dataprovider.User{ + Groups: []sdk.GroupMapping{ + { + Name: groupName, + Type: sdk.GroupTypePrimary, + }, + }, + }) + assert.Error(t, err) dataRetentionAction := dataprovider.BaseEventAction{ Type: dataprovider.ActionTypeDataRetentionCheck, @@ -633,6 +668,60 @@ func TestEventRuleActions(t *testing.T) { if assert.Error(t, err) { assert.Contains(t, err.Error(), "no transfer quota reset executed") } + action.Type = dataprovider.ActionTypeFilesystem + action.Options = dataprovider.BaseEventActionOptions{ + FsConfig: dataprovider.EventActionFilesystemConfig{ + Type: dataprovider.FilesystemActionRename, + Renames: []dataprovider.KeyValue{ + { + Key: "/source", + Value: "/target", + }, + }, + }, + } + err = executeRuleAction(action, EventParams{}, dataprovider.ConditionOptions{ + Names: []dataprovider.ConditionPattern{ + { + Pattern: "no match", + }, + }, + }) + if assert.Error(t, err) { + assert.Contains(t, err.Error(), "no rename executed") + } + action.Options = dataprovider.BaseEventActionOptions{ + FsConfig: dataprovider.EventActionFilesystemConfig{ + Type: dataprovider.FilesystemActionDelete, + Deletes: []string{"/dir1"}, + }, + } + err = executeRuleAction(action, EventParams{}, dataprovider.ConditionOptions{ + Names: []dataprovider.ConditionPattern{ + { + Pattern: "no match", + }, + }, + }) + if assert.Error(t, err) { + assert.Contains(t, err.Error(), "no delete executed") + } + action.Options = dataprovider.BaseEventActionOptions{ + FsConfig: dataprovider.EventActionFilesystemConfig{ + Type: dataprovider.FilesystemActionMkdirs, + Deletes: []string{"/dir1"}, + }, + } + err = executeRuleAction(action, EventParams{}, dataprovider.ConditionOptions{ + Names: []dataprovider.ConditionPattern{ + { + Pattern: "no match", + }, + }, + }) + if assert.Error(t, err) { + assert.Contains(t, err.Error(), "no mkdir executed") + } err = dataprovider.DeleteUser(username1, "", "") assert.NoError(t, err) @@ -712,19 +801,12 @@ func TestEventRuleActions(t *testing.T) { } func TestFilesystemActionErrors(t *testing.T) { - err := executeFsRuleAction(dataprovider.EventActionFilesystemConfig{}, EventParams{}) + err := executeFsRuleAction(dataprovider.EventActionFilesystemConfig{}, dataprovider.ConditionOptions{}, EventParams{}) if assert.Error(t, err) { assert.Contains(t, err.Error(), "unsupported filesystem action") } username := "test_user_for_actions" testReplacer := strings.NewReplacer("old", "new") - err = executeDeleteFsAction(nil, testReplacer, username) - assert.Error(t, err) - err = executeMkDirsFsAction(nil, testReplacer, username) - assert.Error(t, err) - err = executeRenameFsAction(nil, testReplacer, username) - assert.Error(t, err) - user := dataprovider.User{ BaseUser: sdk.BaseUser{ Username: username, @@ -750,11 +832,11 @@ func TestFilesystemActionErrors(t *testing.T) { err = dataprovider.AddUser(&user, "", "") assert.NoError(t, err) // check root fs fails - err = executeDeleteFsAction(nil, testReplacer, username) + err = executeDeleteFsActionForUser(nil, testReplacer, user) assert.Error(t, err) - err = executeMkDirsFsAction(nil, testReplacer, username) + err = executeMkDirsFsActionForUser(nil, testReplacer, user) assert.Error(t, err) - err = executeRenameFsAction(nil, testReplacer, username) + err = executeRenameFsActionForUser(nil, testReplacer, user) assert.Error(t, err) user.FsConfig.Provider = sdk.LocalFilesystemProvider @@ -763,15 +845,36 @@ func TestFilesystemActionErrors(t *testing.T) { assert.NoError(t, err) err = dataprovider.AddUser(&user, "", "") assert.NoError(t, err) - err = executeRenameFsAction([]dataprovider.KeyValue{ + err = executeRenameFsActionForUser([]dataprovider.KeyValue{ { Key: "/p1", Value: "/p1", }, - }, testReplacer, username) + }, testReplacer, user) if assert.Error(t, err) { assert.Contains(t, err.Error(), "the rename source and target cannot be the same") } + err = executeRuleAction(dataprovider.BaseEventAction{ + Type: dataprovider.ActionTypeFilesystem, + Options: dataprovider.BaseEventActionOptions{ + FsConfig: dataprovider.EventActionFilesystemConfig{ + Type: dataprovider.FilesystemActionRename, + Renames: []dataprovider.KeyValue{ + { + Key: "/p2", + Value: "/p2", + }, + }, + }, + }, + }, EventParams{}, dataprovider.ConditionOptions{ + Names: []dataprovider.ConditionPattern{ + { + Pattern: username, + }, + }, + }) + assert.Error(t, err) if runtime.GOOS != osWindows { dirPath := filepath.Join(user.HomeDir, "adir", "sub") @@ -783,26 +886,59 @@ func TestFilesystemActionErrors(t *testing.T) { err = os.Chmod(dirPath, 0001) assert.NoError(t, err) - err = executeDeleteFsAction([]string{"/adir/sub"}, testReplacer, username) + err = executeDeleteFsActionForUser([]string{"/adir/sub"}, testReplacer, user) assert.Error(t, err) - err = executeDeleteFsAction([]string{"/adir/sub/f.dat"}, testReplacer, username) + err = executeDeleteFsActionForUser([]string{"/adir/sub/f.dat"}, testReplacer, user) assert.Error(t, err) err = os.Chmod(dirPath, 0555) assert.NoError(t, err) - err = executeDeleteFsAction([]string{"/adir/sub/f.dat"}, testReplacer, username) + err = executeDeleteFsActionForUser([]string{"/adir/sub/f.dat"}, testReplacer, user) if assert.Error(t, err) { assert.Contains(t, err.Error(), "unable to remove file") } + err = executeRuleAction(dataprovider.BaseEventAction{ + Type: dataprovider.ActionTypeFilesystem, + Options: dataprovider.BaseEventActionOptions{ + FsConfig: dataprovider.EventActionFilesystemConfig{ + Type: dataprovider.FilesystemActionDelete, + Deletes: []string{"/adir/sub/f.dat"}, + }, + }, + }, EventParams{}, dataprovider.ConditionOptions{ + Names: []dataprovider.ConditionPattern{ + { + Pattern: username, + }, + }, + }) + assert.Error(t, err) - err = executeMkDirsFsAction([]string{"/adir/sub/sub"}, testReplacer, username) + err = executeMkDirsFsActionForUser([]string{"/adir/sub/sub"}, testReplacer, user) if assert.Error(t, err) { assert.Contains(t, err.Error(), "unable to create dir") } - err = executeMkDirsFsAction([]string{"/adir/sub/sub/sub"}, testReplacer, username) + err = executeMkDirsFsActionForUser([]string{"/adir/sub/sub/sub"}, testReplacer, user) if assert.Error(t, err) { assert.Contains(t, err.Error(), "unable to check parent dirs") } + err = executeRuleAction(dataprovider.BaseEventAction{ + Type: dataprovider.ActionTypeFilesystem, + Options: dataprovider.BaseEventActionOptions{ + FsConfig: dataprovider.EventActionFilesystemConfig{ + Type: dataprovider.FilesystemActionMkdirs, + MkDirs: []string{"/adir/sub/sub1"}, + }, + }, + }, EventParams{}, dataprovider.ConditionOptions{ + Names: []dataprovider.ConditionPattern{ + { + Pattern: username, + }, + }, + }) + assert.Error(t, err) + err = os.Chmod(dirPath, os.ModePerm) assert.NoError(t, err) } @@ -944,17 +1080,6 @@ func TestScheduledActions(t *testing.T) { job.Run() assert.DirExists(t, backupsPath) - action.Type = dataprovider.ActionTypeFilesystem - action.Options = dataprovider.BaseEventActionOptions{ - FsConfig: dataprovider.EventActionFilesystemConfig{ - Type: dataprovider.FilesystemActionMkdirs, - MkDirs: []string{"/dir"}, - }, - } - err = dataprovider.UpdateEventAction(action, "", "") - assert.NoError(t, err) - job.Run() // action is not compatible with a scheduled rule - err = dataprovider.DeleteEventRule(rule.Name, "", "") assert.NoError(t, err) err = dataprovider.DeleteEventAction(action.Name, "", "") diff --git a/internal/dataprovider/eventrule.go b/internal/dataprovider/eventrule.go index 4d396d59..1c2a5a5f 100644 --- a/internal/dataprovider/eventrule.go +++ b/internal/dataprovider/eventrule.go @@ -1062,14 +1062,6 @@ func (r *EventRule) CheckActionsConsistency(providerObjectType string) error { action.Name, getActionTypeAsString(action.Type)) } } - case EventTriggerSchedule: - // to execute a filesystem action we need a user - for _, action := range r.Actions { - if action.Type == ActionTypeFilesystem { - return fmt.Errorf("action %q, type %q is not supported for scheduled events", - action.Name, getActionTypeAsString(action.Type)) - } - } case EventTriggerIPBlocked, EventTriggerCertificate: if err := r.checkIPBlockedAndCertificateActions(); err != nil { return err diff --git a/pkgs/build.sh b/pkgs/build.sh index 94874e9f..1f7a948e 100755 --- a/pkgs/build.sh +++ b/pkgs/build.sh @@ -1,6 +1,6 @@ #!/bin/bash -NFPM_VERSION=2.17.0 +NFPM_VERSION=2.18.1 NFPM_ARCH=${NFPM_ARCH:-amd64} if [ -z ${SFTPGO_VERSION} ] then