mirror of
https://github.com/drakkan/sftpgo.git
synced 2024-11-21 23:20:24 +00:00
EventManager: add IDP login trigger and check account action
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
parent
40344ec0ff
commit
e29f6857db
58 changed files with 1660 additions and 385 deletions
|
@ -15,6 +15,7 @@ The following actions are supported:
|
|||
- `Metadata check`. A metadata check requires a metadata plugin such as [this one](https://github.com/sftpgo/sftpgo-plugin-metadata) and removes the metadata associated to missing items (for example objects deleted outside SFTPGo). A metadata check does nothing is no metadata plugin is installed or external metadata are not supported for a filesystem.
|
||||
- `Password expiration check`. You can send an email notification to users whose password is about to expire.
|
||||
- `User expiration check`. You can receive notifications with expired users.
|
||||
- `Identity Provider account check`. You can create/update accounts for users/admins logging in using an Identity Provider.
|
||||
- `Filesystem`. For these actions, the required permissions are automatically granted. This is the same as executing the actions from an SFTP client and the same restrictions applies. Supported actions:
|
||||
- `Rename`. You can rename one or more files or directories.
|
||||
- `Delete`. You can delete one or more files and directories.
|
||||
|
@ -47,6 +48,7 @@ The following placeholders are supported:
|
|||
- `{{Timestamp}}`. Event timestamp as nanoseconds since epoch.
|
||||
- `{{ObjectData}}`. Provider object data serialized as JSON with sensitive fields removed.
|
||||
- `{{RetentionReports}}`. Data retention reports as zip compressed CSV files. Supported as email attachment, file path for multipart HTTP request and as single parameter for HTTP requests body. Data retention reports contain details on the number of files deleted and the total size deleted for each folder.
|
||||
- `{{IDPField<fieldname>}}`. Identity Provider custom fields containing a string.
|
||||
|
||||
Event rules are based on the premise that an event occours. To each rule you can associate one or more actions.
|
||||
The following trigger events are supported:
|
||||
|
@ -57,6 +59,7 @@ The following trigger events are supported:
|
|||
- `IP Blocked`, this event can be generated if you enable the [defender](./defender.md).
|
||||
- `Certificate`, this event is generated when a certificate is renewed using the built-in ACME protocol. Both successful and failed renewals are notified.
|
||||
- `On demand`, this trigger is generated manually using the WebAdmin or the REST API.
|
||||
- `Identity Provider login`, this trigger is generated when a user/admin logs in using an external Identity Provider.
|
||||
|
||||
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.
|
||||
|
||||
|
|
|
@ -121,6 +121,7 @@ And the following is an example ID token which allows the SFTPGo user `user1` to
|
|||
```
|
||||
|
||||
SFTPGo users (not admins) can be created/updated after successful OpenID authentication by defining a [pre-login hook](./dynamic-user-mod.md).
|
||||
Users and admins can also be created/updated after successful OpenID authentication using the [EventManager](./eventmanager.md).
|
||||
You can use `scopes` configuration to request additional information (claims) about authenticated users (See your provider's own documentation for more information).
|
||||
By default the scopes `"openid", "profile", "email"` are retrieved.
|
||||
The `custom_fields` configuration parameter can be used to define claim field names to pass to the pre-login hook,
|
||||
|
@ -165,3 +166,5 @@ The pre-login hook will receive a JSON serialized user with the following field:
|
|||
},
|
||||
...
|
||||
```
|
||||
|
||||
In EventManager actions you can use the placeholder `{{IDPFieldsftpgo_home_dir}}` for string-based custom fields.
|
||||
|
|
64
go.mod
64
go.mod
|
@ -3,22 +3,22 @@ module github.com/drakkan/sftpgo/v2
|
|||
go 1.20
|
||||
|
||||
require (
|
||||
cloud.google.com/go/storage v1.29.0
|
||||
cloud.google.com/go/storage v1.30.1
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.4.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.0.0
|
||||
github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962
|
||||
github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5
|
||||
github.com/alexedwards/argon2id v0.0.0-20230305115115-4b3c3280a736
|
||||
github.com/aws/aws-sdk-go-v2 v1.17.6
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.17
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.17
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.0
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.57
|
||||
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.14.6
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.30.6
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.0
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.18.6
|
||||
github.com/aws/aws-sdk-go-v2 v1.17.7
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.19
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.18
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.1
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.59
|
||||
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.14.7
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.31.0
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.1
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.18.7
|
||||
github.com/bmatcuk/doublestar/v4 v4.6.0
|
||||
github.com/cockroachdb/cockroach-go/v2 v2.3.2
|
||||
github.com/cockroachdb/cockroach-go/v2 v2.3.3
|
||||
github.com/coreos/go-oidc/v3 v3.5.0
|
||||
github.com/drakkan/webdav v0.0.0-20230227175313-32996838bcd8
|
||||
github.com/eikenb/pipeat v0.0.0-20210730190139-06b3e6902001
|
||||
|
@ -32,13 +32,13 @@ require (
|
|||
github.com/golang/mock v1.6.0
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/hashicorp/go-hclog v1.4.0
|
||||
github.com/hashicorp/go-hclog v1.5.0
|
||||
github.com/hashicorp/go-plugin v1.4.10-0.20230306173702-d78f3fc2891d
|
||||
github.com/hashicorp/go-retryablehttp v0.7.2
|
||||
github.com/jackc/pgx/v5 v5.3.2-0.20230311213408-9ae852eb583d
|
||||
github.com/jlaffaye/ftp v0.0.0-20201112195030-9aae4d151126
|
||||
github.com/klauspost/compress v1.16.3
|
||||
github.com/lestrrat-go/jwx/v2 v2.0.8
|
||||
github.com/lestrrat-go/jwx/v2 v2.0.9
|
||||
github.com/lithammer/shortuuid/v3 v3.0.7
|
||||
github.com/mattn/go-sqlite3 v1.14.16
|
||||
github.com/mhale/smtpd v0.8.0
|
||||
|
@ -62,10 +62,10 @@ require (
|
|||
github.com/subosito/gotenv v1.4.2
|
||||
github.com/unrolled/secure v1.13.0
|
||||
github.com/wagslane/go-password-validator v0.3.0
|
||||
github.com/wneessen/go-mail v0.3.8
|
||||
github.com/wneessen/go-mail v0.3.9
|
||||
github.com/yl2chen/cidranger v1.0.3-0.20210928021809-d1cb2c52f37a
|
||||
go.etcd.io/bbolt v1.3.7
|
||||
go.uber.org/automaxprocs v1.5.1
|
||||
go.uber.org/automaxprocs v1.5.2
|
||||
gocloud.dev v0.29.0
|
||||
golang.org/x/crypto v0.7.0
|
||||
golang.org/x/net v0.8.0
|
||||
|
@ -73,7 +73,7 @@ require (
|
|||
golang.org/x/sys v0.6.0
|
||||
golang.org/x/term v0.6.0
|
||||
golang.org/x/time v0.3.0
|
||||
google.golang.org/api v0.112.0
|
||||
google.golang.org/api v0.114.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||
)
|
||||
|
||||
|
@ -81,20 +81,20 @@ require (
|
|||
cloud.google.com/go v0.110.0 // indirect
|
||||
cloud.google.com/go/compute v1.18.0 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||
cloud.google.com/go/iam v0.12.0 // indirect
|
||||
cloud.google.com/go/iam v0.13.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.2.0 // indirect
|
||||
github.com/ajg/form v1.5.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.30 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.24 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.31 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.22 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.31 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.25 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.32 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.23 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.25 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.24 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.24 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.12.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.26 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.25 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.12.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.6 // indirect
|
||||
github.com/aws/smithy-go v1.13.5 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/boombuler/barcode v1.0.1 // indirect
|
||||
|
@ -108,7 +108,7 @@ require (
|
|||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
github.com/go-jose/go-jose/v3 v3.0.0 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/goccy/go-json v0.10.1 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
|
@ -131,7 +131,7 @@ require (
|
|||
github.com/lufia/plan9stats v0.0.0-20230110061619-bbe2e5e100de // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||
github.com/mattn/go-isatty v0.0.18 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/miekg/dns v1.1.52 // indirect
|
||||
github.com/minio/sha256-simd v1.0.0 // indirect
|
||||
|
@ -157,9 +157,9 @@ require (
|
|||
golang.org/x/tools v0.7.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-20230306155012-7f2fa6fef1f4 // indirect
|
||||
google.golang.org/grpc v1.53.0 // indirect
|
||||
google.golang.org/protobuf v1.29.1 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230320184635-7606e756e683 // indirect
|
||||
google.golang.org/grpc v1.54.0 // indirect
|
||||
google.golang.org/protobuf v1.30.0 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
|
126
go.sum
126
go.sum
|
@ -217,8 +217,8 @@ cloud.google.com/go/iam v0.6.0/go.mod h1:+1AH33ueBne5MzYccyMHtEKqLE4/kJOibtffMHD
|
|||
cloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQEYOeg=
|
||||
cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE=
|
||||
cloud.google.com/go/iam v0.10.0/go.mod h1:nXAECrMt2qHpF6RZUZseteD6QyanL68reN4OXPw0UWM=
|
||||
cloud.google.com/go/iam v0.12.0 h1:DRtTY29b75ciH6Ov1PHb4/iat2CLCvrOm40Q0a6DFpE=
|
||||
cloud.google.com/go/iam v0.12.0/go.mod h1:knyHGviacl11zrtZUoDuYpDgLjvr28sLQaG0YB2GYAY=
|
||||
cloud.google.com/go/iam v0.13.0 h1:+CmB+K0J/33d0zSQ9SlFWUeCCEn5XJA0ZMZ3pHE9u8k=
|
||||
cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0=
|
||||
cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc=
|
||||
cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A=
|
||||
cloud.google.com/go/ids v1.1.0/go.mod h1:WIuwCaYVOzHIj2OhN9HAwvW+DBdmUAdcWlFxRl+KubM=
|
||||
|
@ -368,8 +368,9 @@ cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq
|
|||
cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc=
|
||||
cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s=
|
||||
cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y=
|
||||
cloud.google.com/go/storage v1.29.0 h1:6weCgzRvMg7lzuUurI4697AqIRPU1SvzHhynwpW31jI=
|
||||
cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4=
|
||||
cloud.google.com/go/storage v1.30.1 h1:uOdMxAs8HExqBlnLtnQyP0YkvbiDpdGShGKtx6U/oNM=
|
||||
cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E=
|
||||
cloud.google.com/go/storagetransfer v1.5.0/go.mod h1:dxNzUopWy7RQevYFHewchb29POFv3/AaBgnhqzqiK0w=
|
||||
cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I=
|
||||
cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw=
|
||||
|
@ -478,8 +479,8 @@ github.com/AzureAD/microsoft-authentication-library-for-go v0.8.1/go.mod h1:4qFo
|
|||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
|
||||
github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962 h1:KeNholpO2xKjgaaSyd+DyQRrsQjhbSeS7qe4nEw8aQw=
|
||||
github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962/go.mod h1:kC29dT1vFpj7py2OvG1khBdQpo3kInWP+6QipLbdngo=
|
||||
github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5 h1:IEjq88XO4PuBDcvmjQJcQGg+w+UaafSy8G5Kcb5tBhI=
|
||||
github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5/go.mod h1:exZ0C/1emQJAw5tHOaUDyY1ycttqBAPcxuzf7QbY6ec=
|
||||
github.com/GoogleCloudPlatform/cloudsql-proxy v1.33.2/go.mod h1:uqoR4sJc63p7ugW8a/vsEspOsNuehbi7ptS2CHCyOnY=
|
||||
github.com/HdrHistogram/hdrhistogram-go v1.1.0/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
|
||||
github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
|
||||
|
@ -556,67 +557,67 @@ github.com/aws/aws-sdk-go v1.44.187/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8
|
|||
github.com/aws/aws-sdk-go v1.44.200/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
|
||||
github.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4=
|
||||
github.com/aws/aws-sdk-go-v2 v1.17.4/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
|
||||
github.com/aws/aws-sdk-go-v2 v1.17.6 h1:Y773UK7OBqhzi5VDXMi1zVGsoj+CVHs2eaC2bDsLwi0=
|
||||
github.com/aws/aws-sdk-go-v2 v1.17.6/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
|
||||
github.com/aws/aws-sdk-go-v2 v1.17.7 h1:CLSjnhJSTSogvqUGhIC6LqFKATMRexcxLZ0i/Nzk9Eg=
|
||||
github.com/aws/aws-sdk-go-v2 v1.17.7/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 h1:dK82zF6kkPeCo8J1e+tGx4JdvDIQzj7ygIoLg8WMuGs=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10/go.mod h1:VeTZetY5KRJLuD/7fkQXMU6Mw7H5m/KP2J5Iy9osMno=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.12/go.mod h1:J36fOhj1LQBr+O4hJCiT8FwVvieeoSGOtPuvhKlsNu8=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.17 h1:jwTkhULSrbr/SQA8tfdYqZxpG8YsRycmIXxJcbrqY5E=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.17/go.mod h1:Lj3E7XcxJnxMa+AYo89YiL68s1cFJRGduChynYU67VA=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.19 h1:AqFK6zFNtq4i1EYu+eC7lcKHYnZagMn6SW171la0bGw=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.19/go.mod h1:XvTmGMY8d52ougvakOv1RpiTLPz9dlG/OQHsKU/cMmY=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.12/go.mod h1:37HG2MBroXK3jXfxVGtbM2J48ra2+Ltu+tmwr/jO0KA=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.17 h1:IubQO/RNeIVKF5Jy77w/LfUvmmCxTnk2TP1UZZIMiF4=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.17/go.mod h1:K9xeFo1g/YPMguMUD69YpwB4Nyi6W/5wn706xIInJFg=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.18 h1:EQMdtHwz0ILTW1hoP+EwuWhwCG1hD6l3+RWFQABET4c=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.18/go.mod h1:vnwlwjIe+3XJPBYKu1et30ZPABG3VaXJYr8ryohpIyM=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.22/go.mod h1:YGSIJyQ6D6FjKMQh16hVFSIUD54L4F7zTGePqYMYYJU=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.0 h1:/2Cb3SK3xVOQA7Xfr5nCWCo5H3UiNINtsVvVdk8sQqA=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.0/go.mod h1:neYVaeKr5eT7BzwULuG2YbLhzWZ22lpjKdCybR7AXrQ=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.1 h1:gt57MN3liKiyGopcqgNzJb2+d9MJaKT/q1OksHNXVE4=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.1/go.mod h1:lfUx8puBRdM5lVVMQlwt2v+ofiG/X6Ms+dy0UkG/kXw=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.51/go.mod h1:7Grl2gV+dx9SWrUIgwwlUvU40t7+lOSbx34XwfmsTkY=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.57 h1:ubKS0iZH5veiqb44qeHzaoKNPvCZQeBVFw4JDhfeWjk=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.57/go.mod h1:dRBjXtcjmYglxVHpdoGGVWvZumDC27I2GLDGI0Uw4RQ=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.59 h1:E3Y+OfzOK1+rmRo/K2G0ml8Vs+Xqk0kOnf4nS0kUtBc=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.59/go.mod h1:1M4PLSBUVfBI0aP+C9XI7SM6kZPCGYyI6izWz0TGprE=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.28/go.mod h1:3lwChorpIM/BhImY/hy+Z6jekmN92cXGPI1QJasVPYY=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.30 h1:y+8n9AGDjikyXoMBTRaHHHSaFEB8267ykmvyPodJfys=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.30/go.mod h1:LUBAO3zNXQjoONBKn/kR1y0Q4cj/D02Ts0uHYjcCQLM=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.31 h1:sJLYcS+eZn5EeNINGHSCRAwUJMFVqklwkH36Vbyai7M=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.31/go.mod h1:QT0BqUvX1Bh2ABdTGnjqEjvjzrCfIniM9Sc8zn9Yndo=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.22/go.mod h1:EqK7gVrIGAHyZItrD1D8B0ilgwMD1GiWAmbU4u/JHNk=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.24 h1:r+Kv+SEJquhAZXaJ7G4u44cIwXV3f8K+N482NNAzJZA=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.24/go.mod h1:gAuCezX/gob6BSMbItsSlMb6WZGV7K2+fWOvk8xBSto=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.25 h1:1mnRASEKnkqsntcxHaysxwgVoUUp5dkiB+l3llKnqyg=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.25/go.mod h1:zBHOPwhBc3FlQjQJE/D3IfPWiWaQmT06Vq9aNukDo0k=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.29/go.mod h1:TwuqRBGzxjQJIwH16/fOZodwXt2Zxa9/cwJC5ke4j7s=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.31 h1:hf+Vhp5WtTdcSdE+yEcUz8L73sAzN0R+0jQv+Z51/mI=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.31/go.mod h1:5zUjguZfG5qjhG9/wqmuyHRyUftl2B5Cp6NNxNC6kRA=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.32 h1:p5luUImdIqywn6JpQsW3tq5GNOxKmOnEpybzPx+d1lk=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.32/go.mod h1:XGhIBZDEgfqmFIugclZ6FU7v75nHhBDtzuB4xB/tEi4=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.19/go.mod h1:8W88sW3PjamQpKFUQvHWWKay6ARsNvZnzU7+a4apubw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.22 h1:lTqBRUuy8oLhBsnnVZf14uRbIHPHCrGqg4Plc8gU/1U=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.22/go.mod h1:YsOa3tFriwWNvBPYHXM5ARiU2yqBNWPWeUiq+4i7Na0=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.23 h1:DWYZIsyqagnWL00f8M/SOr9fN063OEQWn9LLTbdYXsk=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.23/go.mod h1:uIiFgURZbACBEQJfqTZPb/jxO7R+9LeoHUFudtIdeQI=
|
||||
github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 h1:y2+VQzC6Zh2ojtV2LoC0MNwHWc6qXv/j2vrQtlftkdA=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11/go.mod h1:iV4q2hsqtNECrfmlXyord9u4zyuFEJX9eLgLpSPzWA8=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.23/go.mod h1:1jcUfF+FAOEwtIcNiHPaV4TSoZqkUIPzrohmD7fb95c=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.25 h1:B/hO3jfWRm7hP00UeieNlI5O2xP5WJ27tyJG5lzc7AM=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.25/go.mod h1:54K1zgxK/lai3a4HosE4IKBwZsP/5YAJ6dzJfwsjJ0U=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.26 h1:CeuSeq/8FnYpPtnuIeLQEEvDv9zUjneuYi8EghMBdwQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.26/go.mod h1:2UqAAwMUXKeRkAHIlDJqvMVgOWkUi/AUXPk/YIe+Dg4=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.22/go.mod h1:xt0Au8yPIwYXf/GYPy/vl4K3CgwhfQMYbrH7DlUUIws=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.24 h1:c5qGfdbCHav6viBwiyDns3OXqhqAbGjfIB4uVu2ayhk=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.24/go.mod h1:HMA4FZG6fyib+NDo5bpIxX1EhYjrAOveZJY2YR0xrNE=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.25 h1:5LHn8JQ0qvjD9L9JhMtylnkcw7j05GDZqM9Oin6hpr0=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.25/go.mod h1:/95IA+0lMnzW6XzqYJRpjjsAbKEORVeO0anQqjd2CNU=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.22/go.mod h1:QFVbqK54XArazLvn2wvWMRBi/jGrWii46qbr5DyPGjc=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.24 h1:i4RH8DLv/BHY0fCrXYQDr+DGnWzaxB3Ee/esxUaSavk=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.24/go.mod h1:N8X45/o2cngvjCYi2ZnvI0P4mU4ZRJfEYC3maCSsPyw=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.0 h1:e2ooMhpYGhDnBfSvIyusvAwX7KexuZaHbQY2Dyei7VU=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.0/go.mod h1:bh2E0CXKZsQN+faiKVqC40vfNMAWheoULBCnEgO9K+8=
|
||||
github.com/aws/aws-sdk-go-v2/service/kms v1.20.2/go.mod h1:vdqtUOdVuf5ooy+hJ2GnzqNo94xiAA9s1xbZ1hQgRE0=
|
||||
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.14.6 h1:3yAJmDgUzVGGp5PkHA/HGFquEJRK0uEaep22XZj4UJg=
|
||||
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.14.6/go.mod h1:kFXyTQKLc5KyBUhJ0kUckwncHElnSEbXbBeGpNJUMEY=
|
||||
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.14.7 h1:QA+w3BlShNMgOTRN1kBG2qOIHuTzVTxZ0l3ImKkz+ic=
|
||||
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.14.7/go.mod h1:8Ma9cuRQADU+FwLPYmoSdvUt4tItX2kleWi/++H0BF0=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.30.2/go.mod h1:SXDHd6fI2RhqB7vmAzyYQCTQnpZrIprVJvYxpzW3JAM=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.30.6 h1:zzTm99krKsFcF4N7pu2z17yCcAZpQYZ7jnJZPIgEMXE=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.30.6/go.mod h1:PudwVKUTApfm0nYaPutOXaKdPKTlZYClGBQpVIRdcbs=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.31.0 h1:B1G2pSPvbAtQjilPq+Y7jLIzCOwKzuVEl+aBBaNG0AQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.31.0/go.mod h1:ncltU6n4Nof5uJttDtcNQ537uNuwYqsZZQcpkd2/GUQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.3/go.mod h1:hqPcyOuLU6yWIbLy3qMnQnmidgKuIEwqIlW6+chYnog=
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.0 h1:B4LvuBxrxh2WXakqwJL22EPAWgqGGK9/E4YQV/IIkYo=
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.0/go.mod h1:XF4Gbmcn6V9xIIm6lhwtyX1NXConNJ8x6yizt2Ejx/0=
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.1 h1:+rANS0SbrDUqF3VJeil1HJHhNK8vdUu1VGqnkr4o6kw=
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.1/go.mod h1:SUiYnlcBDUvSLD6iUmwSwXni2i6iGa9WHc+eM5061W4=
|
||||
github.com/aws/aws-sdk-go-v2/service/sns v1.20.2/go.mod h1:VN2n9SOMS1lNbh5YD7o+ho0/rgfifSrK//YYNiVVF5E=
|
||||
github.com/aws/aws-sdk-go-v2/service/sqs v1.20.2/go.mod h1:1ttxGjUHZliCQMpPss1sU5+Ph/5NvdMFRzr96bv8gm0=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssm v1.35.2/go.mod h1:VLSz2SHUKYFSOlXB/GlXoLU6KPYQJAbw7I20TDJdyws=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.12.1/go.mod h1:IgV8l3sj22nQDd5qcAGY0WenwCzCphqdbFOpfktZPrI=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.12.5 h1:bdKIX6SVF3nc3xJFw6Nf0igzS6Ff/louGq8Z6VP/3Hs=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.12.5/go.mod h1:vuWiaDB30M/QTC+lI3Wj6S/zb7tpUK2MSYgy3Guh2L0=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.12.6 h1:5V7DWLBd7wTELVz5bPpwzYy/sikk0gsgZfj40X+l5OI=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.12.6/go.mod h1:Y1VOmit/Fn6Tz1uFAeCO6Q7M2fmfXSCLeL5INVYsLuY=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.1/go.mod h1:O1YSOg3aekZibh2SngvCRRG+cRHKKlYgxf/JBF/Kr/k=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.5 h1:xLPZMyuZ4GuqRCIec/zWuIhRFPXh2UOJdLXBSi64ZWQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.5/go.mod h1:QjxpHmCwAg0ESGtPQnLIVp7SedTOBMYy+Slr3IfMKeI=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.6 h1:B8cauxOH1W1v7rd8RdI/MWnoR4Ze0wIHWrb90qczxj4=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.6/go.mod h1:Lh/bc9XUf8CfOY6Jp5aIkQtN+j1mc+nExc+KXj9jx2s=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.18.3/go.mod h1:b+psTJn33Q4qGoDaM7ZiOVVG8uVjGI6HaZ8WBHdgDgU=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.18.6 h1:rIFn5J3yDoeuKCE9sESXqM5POTAhOP1du3bv/qTL+tE=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.18.6/go.mod h1:48WJ9l3dwP0GSHWGc5sFGGlCkuA82Mc2xnw+T6Q8aDw=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.18.7 h1:bWNgNdRko2x6gqa0blfATqAZKZokPIeM1vfmQt2pnvM=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.18.7/go.mod h1:JuTnSoeePXmMVe9G8NcjjwgOKEfZ4cOjMuT2IBT/2eI=
|
||||
github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
|
||||
github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8=
|
||||
github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
|
||||
|
@ -693,8 +694,8 @@ github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWH
|
|||
github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||
github.com/cockroachdb/cockroach-go/v2 v2.3.2 h1:r+HgkUAPoRtB5UUxnSvmh3irEVeUETHSAjCSxX5Tfto=
|
||||
github.com/cockroachdb/cockroach-go/v2 v2.3.2/go.mod h1:1wNJ45eSXW9AnOc3skntW9ZUZz6gxrQK3cOj3rK+BC8=
|
||||
github.com/cockroachdb/cockroach-go/v2 v2.3.3 h1:fNmtG6XhoA1DhdDCIu66YyGSsNb1szj4CaAsbDxRmy4=
|
||||
github.com/cockroachdb/cockroach-go/v2 v2.3.3/go.mod h1:1wNJ45eSXW9AnOc3skntW9ZUZz6gxrQK3cOj3rK+BC8=
|
||||
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
|
||||
github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo=
|
||||
github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA=
|
||||
|
@ -1062,9 +1063,8 @@ github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY9
|
|||
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
|
||||
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
|
||||
github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/goccy/go-json v0.10.1 h1:lEs5Ob+oOG/Ze199njvzHbhn6p9T+h64F5hRj69iTTo=
|
||||
github.com/goccy/go-json v0.10.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/goccy/go-yaml v1.9.5/go.mod h1:U/jl18uSupI5rdI2jmuCswEA2htH9eXfferR3KfscvA=
|
||||
github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
|
||||
github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
|
||||
|
@ -1278,8 +1278,8 @@ github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39
|
|||
github.com/hashicorp/go-hclog v0.12.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-hclog v1.4.0 h1:ctuWFGrhFha8BnnzxqeRGidlEcQkDyL5u8J8t5eA11I=
|
||||
github.com/hashicorp/go-hclog v1.4.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c=
|
||||
github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-immutable-radix v1.2.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
|
@ -1475,8 +1475,8 @@ github.com/lestrrat-go/httprc v1.0.4 h1:bAZymwoZQb+Oq8MEbyipag7iSq6YIga8Wj6GOiJG
|
|||
github.com/lestrrat-go/httprc v1.0.4/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo=
|
||||
github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI=
|
||||
github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4=
|
||||
github.com/lestrrat-go/jwx/v2 v2.0.8 h1:jCFT8oc0hEDVjgUgsBy1F9cbjsjAVZSXNi7JaU9HR/Q=
|
||||
github.com/lestrrat-go/jwx/v2 v2.0.8/go.mod h1:zLxnyv9rTlEvOUHbc48FAfIL8iYu2hHvIRaTFGc8mT0=
|
||||
github.com/lestrrat-go/jwx/v2 v2.0.9 h1:TRX4Q630UXxPVLvP5vGaqVJO7S+0PE6msRZUsFSBoC8=
|
||||
github.com/lestrrat-go/jwx/v2 v2.0.9/go.mod h1:K68euYaR95FnL0hIQB8VvzL70vB7pSifbJUydCTPmgM=
|
||||
github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
|
||||
github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU=
|
||||
github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
|
||||
|
@ -1529,8 +1529,8 @@ github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOA
|
|||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
||||
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
|
||||
github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
|
||||
github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
|
||||
|
@ -1963,8 +1963,8 @@ github.com/wagslane/go-password-validator v0.3.0 h1:vfxOPzGHkz5S146HDpavl0cw1DSV
|
|||
github.com/wagslane/go-password-validator v0.3.0/go.mod h1:TI1XJ6T5fRdRnHqHt14pvy1tNVnrwe7m3/f1f2fDphQ=
|
||||
github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
|
||||
github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI=
|
||||
github.com/wneessen/go-mail v0.3.8 h1:ja5D/o/RVwrtRIYFlrO7GmtcjDNeMakGQuwQRZYv0JM=
|
||||
github.com/wneessen/go-mail v0.3.8/go.mod h1:m25lkU2GYQnlVr6tdwK533/UXxo57V0kLOjaFYmub0E=
|
||||
github.com/wneessen/go-mail v0.3.9 h1:Q4DbCk3htT5DtDWKeMgNXCiHc4bBY/vv/XQPT6XDXzc=
|
||||
github.com/wneessen/go-mail v0.3.9/go.mod h1:zxOlafWCP/r6FEhAaRgH4IC1vg2YXxO0Nar9u0IScZ8=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||
github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
|
||||
github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
|
||||
|
@ -2080,8 +2080,9 @@ go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
|||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/automaxprocs v1.5.1 h1:e1YG66Lrk73dn4qhg8WFSvhF0JuFQF0ERIp4rpuV8Qk=
|
||||
go.uber.org/automaxprocs v1.5.1/go.mod h1:BF4eumQw0P9GtnuxxovUd06vwm1o18oMzFtK66vU6XU=
|
||||
go.uber.org/automaxprocs v1.5.2 h1:2LxUOGiR3O6tw8ui5sZa2LAaHnsviZdVOUZw4fvbnME=
|
||||
go.uber.org/automaxprocs v1.5.2/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
|
||||
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
|
||||
go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||
|
@ -2655,8 +2656,8 @@ google.golang.org/api v0.106.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/
|
|||
google.golang.org/api v0.107.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY=
|
||||
google.golang.org/api v0.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY=
|
||||
google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI=
|
||||
google.golang.org/api v0.112.0 h1:iDmzvZ4C086R3+en4nSyIf07HlQKMOX1Xx2dmia/+KQ=
|
||||
google.golang.org/api v0.112.0/go.mod h1:737UfWHNsOq4F3REUTmb+GN9pugkgNLCayLTfoIKpPc=
|
||||
google.golang.org/api v0.114.0 h1:1xQPji6cO2E2vLiI+C/XiFAnsn1WV3mjaEwGLhi3grE=
|
||||
google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg=
|
||||
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=
|
||||
|
@ -2801,8 +2802,8 @@ google.golang.org/genproto v0.0.0-20230113154510-dbe35b8444a5/go.mod h1:RGgjbofJ
|
|||
google.golang.org/genproto v0.0.0-20230124163310-31e0e69b6fc2/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
|
||||
google.golang.org/genproto v0.0.0-20230125152338-dcaf20b6aeaa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
|
||||
google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
|
||||
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 h1:DdoeryqhaXp1LtT/emMP1BRJPHHKFi5akj/nbx/zNTA=
|
||||
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s=
|
||||
google.golang.org/genproto v0.0.0-20230320184635-7606e756e683 h1:khxVcsk/FhnzxMKOyD+TDGwjbEOpcPuIpmafPGFmhMA=
|
||||
google.golang.org/genproto v0.0.0-20230320184635-7606e756e683/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s=
|
||||
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
|
@ -2846,8 +2847,9 @@ google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCD
|
|||
google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
|
||||
google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww=
|
||||
google.golang.org/grpc v1.52.1/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY=
|
||||
google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc=
|
||||
google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw=
|
||||
google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag=
|
||||
google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g=
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
|
@ -2864,8 +2866,8 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
|
|||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.29.1 h1:7QBf+IK2gx70Ap/hDsOmam3GE0v9HicjfEdAxE62UoM=
|
||||
google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
|
|
@ -287,10 +287,7 @@ func (c *Configuration) validateChallenges() error {
|
|||
if err := c.HTTP01Challenge.validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.TLSALPN01Challenge.validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return c.TLSALPN01Challenge.validate()
|
||||
}
|
||||
|
||||
func (c *Configuration) checkDomains() {
|
||||
|
|
|
@ -321,7 +321,7 @@ type actionHandlerStub struct {
|
|||
called bool
|
||||
}
|
||||
|
||||
func (h *actionHandlerStub) Handle(event *notifier.FsEvent) (int, error) {
|
||||
func (h *actionHandlerStub) Handle(_ *notifier.FsEvent) (int, error) {
|
||||
h.called = true
|
||||
|
||||
return 1, nil
|
||||
|
|
|
@ -1235,7 +1235,7 @@ func TestParseAllowedIPAndRanges(t *testing.T) {
|
|||
assert.False(t, allow[1](net.ParseIP("172.16.1.1")))
|
||||
}
|
||||
|
||||
func TestHideConfidentialData(t *testing.T) {
|
||||
func TestHideConfidentialData(_ *testing.T) {
|
||||
for _, provider := range []sdk.FilesystemProvider{sdk.LocalFilesystemProvider,
|
||||
sdk.CryptedFilesystemProvider, sdk.S3FilesystemProvider, sdk.GCSFilesystemProvider,
|
||||
sdk.AzureBlobFilesystemProvider, sdk.SFTPFilesystemProvider,
|
||||
|
|
|
@ -63,7 +63,7 @@ func (fs *MockOsFs) IsUploadResumeSupported() bool {
|
|||
return !fs.hasVirtualFolders
|
||||
}
|
||||
|
||||
func (fs *MockOsFs) Chtimes(name string, atime, mtime time.Time, isUploading bool) error {
|
||||
func (fs *MockOsFs) Chtimes(_ string, _, _ time.Time, _ bool) error {
|
||||
return vfs.ErrVfsUnsupported
|
||||
}
|
||||
|
||||
|
@ -75,7 +75,7 @@ func (fs *MockOsFs) Lstat(name string) (os.FileInfo, error) {
|
|||
}
|
||||
|
||||
// Walk returns a duplicate path for testing
|
||||
func (fs *MockOsFs) Walk(root string, walkFn filepath.WalkFunc) error {
|
||||
func (fs *MockOsFs) Walk(_ string, walkFn filepath.WalkFunc) error {
|
||||
if fs.err == errWalkDir {
|
||||
walkFn("fsdpath", vfs.NewFileInfo("dpath", true, 0, time.Now(), false), nil) //nolint:errcheck
|
||||
return walkFn("fsdpath", vfs.NewFileInfo("dpath", true, 0, time.Now(), false), nil) //nolint:errcheck
|
||||
|
|
|
@ -18,6 +18,7 @@ import (
|
|||
"bytes"
|
||||
"context"
|
||||
"encoding/csv"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
@ -56,6 +57,12 @@ const (
|
|||
maxAttachmentsSize = int64(10 * 1024 * 1024)
|
||||
)
|
||||
|
||||
// Supported IDP login events
|
||||
const (
|
||||
IDPLoginUser = "IDP login user"
|
||||
IDPLoginAdmin = "IDP login admin"
|
||||
)
|
||||
|
||||
var (
|
||||
// eventManager handle the supported event rules actions
|
||||
eventManager eventRulesContainer
|
||||
|
@ -90,6 +97,11 @@ func HandleCertificateEvent(params EventParams) {
|
|||
eventManager.handleCertificateEvent(params)
|
||||
}
|
||||
|
||||
// HandleIDPLoginEvent executes actions defined for a successful login from an Identity Provider
|
||||
func HandleIDPLoginEvent(params EventParams, customFields *map[string]any) (*dataprovider.User, *dataprovider.Admin, error) {
|
||||
return eventManager.handleIDPLoginEvent(params, customFields)
|
||||
}
|
||||
|
||||
// eventRulesContainer stores event rules by trigger
|
||||
type eventRulesContainer struct {
|
||||
sync.RWMutex
|
||||
|
@ -99,6 +111,7 @@ type eventRulesContainer struct {
|
|||
Schedules []dataprovider.EventRule
|
||||
IPBlockedEvents []dataprovider.EventRule
|
||||
CertificateEvents []dataprovider.EventRule
|
||||
IPDLoginEvents []dataprovider.EventRule
|
||||
schedulesMapping map[string][]cron.EntryID
|
||||
concurrencyGuard chan struct{}
|
||||
}
|
||||
|
@ -168,6 +181,15 @@ func (r *eventRulesContainer) removeRuleInternal(name string) {
|
|||
return
|
||||
}
|
||||
}
|
||||
for idx := range r.IPDLoginEvents {
|
||||
if r.IPDLoginEvents[idx].Name == name {
|
||||
lastIdx := len(r.IPDLoginEvents) - 1
|
||||
r.IPDLoginEvents[idx] = r.IPDLoginEvents[lastIdx]
|
||||
r.IPDLoginEvents = r.IPDLoginEvents[:lastIdx]
|
||||
eventManagerLog(logger.LevelDebug, "removed rule %q from IDP login events", name)
|
||||
return
|
||||
}
|
||||
}
|
||||
for idx := range r.Schedules {
|
||||
if r.Schedules[idx].Name == name {
|
||||
if schedules, ok := r.schedulesMapping[name]; ok {
|
||||
|
@ -213,6 +235,9 @@ func (r *eventRulesContainer) addUpdateRuleInternal(rule dataprovider.EventRule)
|
|||
case dataprovider.EventTriggerCertificate:
|
||||
r.CertificateEvents = append(r.CertificateEvents, rule)
|
||||
eventManagerLog(logger.LevelDebug, "added rule %q to certificate events", rule.Name)
|
||||
case dataprovider.EventTriggerIDPLogin:
|
||||
r.IPDLoginEvents = append(r.IPDLoginEvents, rule)
|
||||
eventManagerLog(logger.LevelDebug, "added rule %q to IDP login events", rule.Name)
|
||||
case dataprovider.EventTriggerSchedule:
|
||||
for _, schedule := range rule.Conditions.Schedules {
|
||||
cronSpec := schedule.GetCronSpec()
|
||||
|
@ -253,13 +278,27 @@ func (r *eventRulesContainer) loadRules() {
|
|||
r.addUpdateRuleInternal(rule)
|
||||
}
|
||||
}
|
||||
eventManagerLog(logger.LevelDebug, "event rules updated, fs events: %d, provider events: %d, schedules: %d, ip blocked events: %d, certificate events: %d",
|
||||
len(r.FsEvents), len(r.ProviderEvents), len(r.Schedules), len(r.IPBlockedEvents), len(r.CertificateEvents))
|
||||
eventManagerLog(logger.LevelDebug, "event rules updated, fs events: %d, provider events: %d, schedules: %d, ip blocked events: %d, certificate events: %d, IDP login events: %d",
|
||||
len(r.FsEvents), len(r.ProviderEvents), len(r.Schedules), len(r.IPBlockedEvents), len(r.CertificateEvents), len(r.IPDLoginEvents))
|
||||
|
||||
r.setLastLoadTime(modTime)
|
||||
}
|
||||
|
||||
func (r *eventRulesContainer) checkProviderEventMatch(conditions dataprovider.EventConditions, params EventParams) bool {
|
||||
func (*eventRulesContainer) checkIPDLoginEventMatch(conditions *dataprovider.EventConditions, params *EventParams) bool {
|
||||
switch conditions.IDPLoginEvent {
|
||||
case dataprovider.IDPLoginUser:
|
||||
if params.Event != IDPLoginUser {
|
||||
return false
|
||||
}
|
||||
case dataprovider.IDPLoginAdmin:
|
||||
if params.Event != IDPLoginAdmin {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return checkEventConditionPatterns(params.Name, conditions.Options.Names)
|
||||
}
|
||||
|
||||
func (*eventRulesContainer) checkProviderEventMatch(conditions *dataprovider.EventConditions, params *EventParams) bool {
|
||||
if !util.Contains(conditions.ProviderEvents, params.Event) {
|
||||
return false
|
||||
}
|
||||
|
@ -275,7 +314,7 @@ func (r *eventRulesContainer) checkProviderEventMatch(conditions dataprovider.Ev
|
|||
return true
|
||||
}
|
||||
|
||||
func (r *eventRulesContainer) checkFsEventMatch(conditions dataprovider.EventConditions, params EventParams) bool {
|
||||
func (*eventRulesContainer) checkFsEventMatch(conditions *dataprovider.EventConditions, params *EventParams) bool {
|
||||
if !util.Contains(conditions.FsEvents, params.Event) {
|
||||
return false
|
||||
}
|
||||
|
@ -327,7 +366,7 @@ func (r *eventRulesContainer) handleFsEvent(params EventParams) (bool, error) {
|
|||
|
||||
var rulesWithSyncActions, rulesAsync []dataprovider.EventRule
|
||||
for _, rule := range r.FsEvents {
|
||||
if r.checkFsEventMatch(rule.Conditions, params) {
|
||||
if r.checkFsEventMatch(&rule.Conditions, ¶ms) {
|
||||
if err := rule.CheckActionsConsistency(""); err != nil {
|
||||
eventManagerLog(logger.LevelWarn, "rule %q skipped: %v, event %q",
|
||||
rule.Name, err, params.Event)
|
||||
|
@ -361,6 +400,59 @@ func (r *eventRulesContainer) handleFsEvent(params EventParams) (bool, error) {
|
|||
return false, nil
|
||||
}
|
||||
|
||||
func (r *eventRulesContainer) handleIDPLoginEvent(params EventParams, customFields *map[string]any) (*dataprovider.User,
|
||||
*dataprovider.Admin, error,
|
||||
) {
|
||||
r.RLock()
|
||||
|
||||
var rulesWithSyncActions, rulesAsync []dataprovider.EventRule
|
||||
for _, rule := range r.IPDLoginEvents {
|
||||
if r.checkIPDLoginEventMatch(&rule.Conditions, ¶ms) {
|
||||
if err := rule.CheckActionsConsistency(""); err != nil {
|
||||
eventManagerLog(logger.LevelWarn, "rule %q skipped: %v, event %q",
|
||||
rule.Name, err, params.Event)
|
||||
continue
|
||||
}
|
||||
hasSyncActions := false
|
||||
for _, action := range rule.Actions {
|
||||
if action.Options.ExecuteSync {
|
||||
hasSyncActions = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if hasSyncActions {
|
||||
rulesWithSyncActions = append(rulesWithSyncActions, rule)
|
||||
} else {
|
||||
rulesAsync = append(rulesAsync, rule)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r.RUnlock()
|
||||
|
||||
if len(rulesAsync) == 0 && len(rulesWithSyncActions) == 0 {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
params.addIDPCustomFields(customFields)
|
||||
if len(rulesWithSyncActions) > 1 {
|
||||
var ruleNames []string
|
||||
for _, r := range rulesWithSyncActions {
|
||||
ruleNames = append(ruleNames, r.Name)
|
||||
}
|
||||
return nil, nil, fmt.Errorf("more than one account check action rules matches: %q", strings.Join(ruleNames, ","))
|
||||
}
|
||||
|
||||
if len(rulesAsync) > 0 {
|
||||
go executeAsyncRulesActions(rulesAsync, params)
|
||||
}
|
||||
|
||||
if len(rulesWithSyncActions) > 0 {
|
||||
return executeIDPAccountCheckRule(rulesWithSyncActions[0], params)
|
||||
}
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
// username is populated for user objects
|
||||
func (r *eventRulesContainer) handleProviderEvent(params EventParams) {
|
||||
r.RLock()
|
||||
|
@ -368,7 +460,7 @@ func (r *eventRulesContainer) handleProviderEvent(params EventParams) {
|
|||
|
||||
var rules []dataprovider.EventRule
|
||||
for _, rule := range r.ProviderEvents {
|
||||
if r.checkProviderEventMatch(rule.Conditions, params) {
|
||||
if r.checkProviderEventMatch(&rule.Conditions, ¶ms) {
|
||||
if err := rule.CheckActionsConsistency(params.ObjectType); err == nil {
|
||||
rules = append(rules, rule)
|
||||
} else {
|
||||
|
@ -452,6 +544,7 @@ type EventParams struct {
|
|||
IP string
|
||||
Role string
|
||||
Timestamp int64
|
||||
IDPCustomFields *map[string]string
|
||||
Object plugin.Renderer
|
||||
sender string
|
||||
updateStatusFromError bool
|
||||
|
@ -474,10 +567,32 @@ func (p *EventParams) getACopy() *EventParams {
|
|||
retentionChecks = append(retentionChecks, executedCheck)
|
||||
}
|
||||
params.retentionChecks = retentionChecks
|
||||
if p.IDPCustomFields != nil {
|
||||
fields := make(map[string]string)
|
||||
for k, v := range *p.IDPCustomFields {
|
||||
fields[k] = v
|
||||
}
|
||||
params.IDPCustomFields = &fields
|
||||
}
|
||||
|
||||
return ¶ms
|
||||
}
|
||||
|
||||
func (p *EventParams) addIDPCustomFields(customFields *map[string]any) {
|
||||
if customFields == nil {
|
||||
return
|
||||
}
|
||||
|
||||
fields := make(map[string]string)
|
||||
for k, v := range *customFields {
|
||||
switch val := v.(type) {
|
||||
case string:
|
||||
fields[k] = val
|
||||
}
|
||||
}
|
||||
p.IDPCustomFields = &fields
|
||||
}
|
||||
|
||||
// AddError adds a new error to the event params and update the status if needed
|
||||
func (p *EventParams) AddError(err error) {
|
||||
if err == nil {
|
||||
|
@ -622,34 +737,41 @@ func (p *EventParams) getRetentionReportsAsMailAttachment() (*mail.File, error)
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (p *EventParams) getStringReplacements(addObjectData bool) []string {
|
||||
func (*EventParams) getStringReplacement(val string, jsonEscaped bool) string {
|
||||
if jsonEscaped {
|
||||
return util.JSONEscape(val)
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
func (p *EventParams) getStringReplacements(addObjectData, jsonEscaped bool) []string {
|
||||
replacements := []string{
|
||||
"{{Name}}", p.Name,
|
||||
"{{Name}}", p.getStringReplacement(p.Name, jsonEscaped),
|
||||
"{{Event}}", p.Event,
|
||||
"{{Status}}", fmt.Sprintf("%d", p.Status),
|
||||
"{{VirtualPath}}", p.VirtualPath,
|
||||
"{{FsPath}}", p.FsPath,
|
||||
"{{VirtualTargetPath}}", p.VirtualTargetPath,
|
||||
"{{FsTargetPath}}", p.FsTargetPath,
|
||||
"{{ObjectName}}", p.ObjectName,
|
||||
"{{VirtualPath}}", p.getStringReplacement(p.VirtualPath, jsonEscaped),
|
||||
"{{FsPath}}", p.getStringReplacement(p.FsPath, jsonEscaped),
|
||||
"{{VirtualTargetPath}}", p.getStringReplacement(p.VirtualTargetPath, jsonEscaped),
|
||||
"{{FsTargetPath}}", p.getStringReplacement(p.FsTargetPath, jsonEscaped),
|
||||
"{{ObjectName}}", p.getStringReplacement(p.ObjectName, jsonEscaped),
|
||||
"{{ObjectType}}", p.ObjectType,
|
||||
"{{FileSize}}", fmt.Sprintf("%d", p.FileSize),
|
||||
"{{Elapsed}}", fmt.Sprintf("%d", p.Elapsed),
|
||||
"{{Protocol}}", p.Protocol,
|
||||
"{{IP}}", p.IP,
|
||||
"{{Role}}", p.Role,
|
||||
"{{Role}}", p.getStringReplacement(p.Role, jsonEscaped),
|
||||
"{{Timestamp}}", fmt.Sprintf("%d", p.Timestamp),
|
||||
"{{StatusString}}", p.getStatusString(),
|
||||
}
|
||||
if p.VirtualPath != "" {
|
||||
replacements = append(replacements, "{{VirtualDirPath}}", path.Dir(p.VirtualPath))
|
||||
replacements = append(replacements, "{{VirtualDirPath}}", p.getStringReplacement(path.Dir(p.VirtualPath), jsonEscaped))
|
||||
}
|
||||
if p.VirtualTargetPath != "" {
|
||||
replacements = append(replacements, "{{VirtualTargetDirPath}}", path.Dir(p.VirtualTargetPath))
|
||||
replacements = append(replacements, "{{TargetName}}", path.Base(p.VirtualTargetPath))
|
||||
replacements = append(replacements, "{{VirtualTargetDirPath}}", p.getStringReplacement(path.Dir(p.VirtualTargetPath), jsonEscaped))
|
||||
replacements = append(replacements, "{{TargetName}}", p.getStringReplacement(path.Base(p.VirtualTargetPath), jsonEscaped))
|
||||
}
|
||||
if len(p.errors) > 0 {
|
||||
replacements = append(replacements, "{{ErrorString}}", strings.Join(p.errors, ", "))
|
||||
replacements = append(replacements, "{{ErrorString}}", p.getStringReplacement(strings.Join(p.errors, ", "), jsonEscaped))
|
||||
} else {
|
||||
replacements = append(replacements, "{{ErrorString}}", "")
|
||||
}
|
||||
|
@ -660,6 +782,11 @@ func (p *EventParams) getStringReplacements(addObjectData bool) []string {
|
|||
replacements[len(replacements)-1] = string(data)
|
||||
}
|
||||
}
|
||||
if p.IDPCustomFields != nil {
|
||||
for k, v := range *p.IDPCustomFields {
|
||||
replacements = append(replacements, fmt.Sprintf("{{IDPField%s}}", k), p.getStringReplacement(v, jsonEscaped))
|
||||
}
|
||||
}
|
||||
return replacements
|
||||
}
|
||||
|
||||
|
@ -1060,7 +1187,7 @@ func getHTTPRuleActionEndpoint(c dataprovider.EventActionHTTPConfig, replacer *s
|
|||
}
|
||||
|
||||
func writeHTTPPart(m *multipart.Writer, part dataprovider.HTTPPart, h textproto.MIMEHeader,
|
||||
conn *BaseConnection, replacer *strings.Replacer, params *EventParams,
|
||||
conn *BaseConnection, replacer *strings.Replacer, params *EventParams, addObjectData bool,
|
||||
) error {
|
||||
partWriter, err := m.CreatePart(h)
|
||||
if err != nil {
|
||||
|
@ -1068,7 +1195,14 @@ func writeHTTPPart(m *multipart.Writer, part dataprovider.HTTPPart, h textproto.
|
|||
return err
|
||||
}
|
||||
if part.Body != "" {
|
||||
_, err = partWriter.Write([]byte(replaceWithReplacer(part.Body, replacer)))
|
||||
cType := h.Get("Content-Type")
|
||||
if strings.Contains(strings.ToLower(cType), "application/json") {
|
||||
replacements := params.getStringReplacements(addObjectData, true)
|
||||
jsonReplacer := strings.NewReplacer(replacements...)
|
||||
_, err = partWriter.Write([]byte(replaceWithReplacer(part.Body, jsonReplacer)))
|
||||
} else {
|
||||
_, err = partWriter.Write([]byte(replaceWithReplacer(part.Body, replacer)))
|
||||
}
|
||||
if err != nil {
|
||||
eventManagerLog(logger.LevelError, "unable to write part %q, err: %v", part.Name, err)
|
||||
return err
|
||||
|
@ -1095,8 +1229,8 @@ func writeHTTPPart(m *multipart.Writer, part dataprovider.HTTPPart, h textproto.
|
|||
return nil
|
||||
}
|
||||
|
||||
func getHTTPRuleActionBody(c dataprovider.EventActionHTTPConfig, replacer *strings.Replacer,
|
||||
cancel context.CancelFunc, user dataprovider.User, params *EventParams,
|
||||
func getHTTPRuleActionBody(c *dataprovider.EventActionHTTPConfig, replacer *strings.Replacer,
|
||||
cancel context.CancelFunc, user dataprovider.User, params *EventParams, addObjectData bool,
|
||||
) (io.ReadCloser, string, error) {
|
||||
var body io.ReadCloser
|
||||
if c.Method == http.MethodGet {
|
||||
|
@ -1110,6 +1244,11 @@ func getHTTPRuleActionBody(c dataprovider.EventActionHTTPConfig, replacer *strin
|
|||
}
|
||||
return io.NopCloser(bytes.NewBuffer(data)), "", nil
|
||||
}
|
||||
if c.HasJSONBody() {
|
||||
replacements := params.getStringReplacements(addObjectData, true)
|
||||
jsonReplacer := strings.NewReplacer(replacements...)
|
||||
return io.NopCloser(bytes.NewBufferString(replaceWithReplacer(c.Body, jsonReplacer))), "", nil
|
||||
}
|
||||
return io.NopCloser(bytes.NewBufferString(replaceWithReplacer(c.Body, replacer))), "", nil
|
||||
}
|
||||
if len(c.Parts) > 0 {
|
||||
|
@ -1154,7 +1293,7 @@ func getHTTPRuleActionBody(c dataprovider.EventActionHTTPConfig, replacer *strin
|
|||
for _, keyVal := range part.Headers {
|
||||
h.Set(keyVal.Key, replaceWithReplacer(keyVal.Value, replacer))
|
||||
}
|
||||
if err := writeHTTPPart(m, part, h, conn, replacer, params); err != nil {
|
||||
if err := writeHTTPPart(m, part, h, conn, replacer, params, addObjectData); err != nil {
|
||||
cancel()
|
||||
return
|
||||
}
|
||||
|
@ -1176,7 +1315,7 @@ func executeHTTPRuleAction(c dataprovider.EventActionHTTPConfig, params *EventPa
|
|||
addObjectData = c.HasObjectData()
|
||||
}
|
||||
|
||||
replacements := params.getStringReplacements(addObjectData)
|
||||
replacements := params.getStringReplacements(addObjectData, false)
|
||||
replacer := strings.NewReplacer(replacements...)
|
||||
endpoint, err := getHTTPRuleActionEndpoint(c, replacer)
|
||||
if err != nil {
|
||||
|
@ -1193,7 +1332,7 @@ func executeHTTPRuleAction(c dataprovider.EventActionHTTPConfig, params *EventPa
|
|||
return err
|
||||
}
|
||||
}
|
||||
body, contentType, err := getHTTPRuleActionBody(c, replacer, cancel, user, params)
|
||||
body, contentType, err := getHTTPRuleActionBody(&c, replacer, cancel, user, params, addObjectData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1244,7 +1383,7 @@ func executeCommandRuleAction(c dataprovider.EventActionCommandConfig, params *E
|
|||
}
|
||||
}
|
||||
}
|
||||
replacements := params.getStringReplacements(addObjectData)
|
||||
replacements := params.getStringReplacements(addObjectData, false)
|
||||
replacer := strings.NewReplacer(replacements...)
|
||||
|
||||
args := make([]string, 0, len(c.Args))
|
||||
|
@ -1277,7 +1416,7 @@ func executeEmailRuleAction(c dataprovider.EventActionEmailConfig, params *Event
|
|||
addObjectData = true
|
||||
}
|
||||
}
|
||||
replacements := params.getStringReplacements(addObjectData)
|
||||
replacements := params.getStringReplacements(addObjectData, false)
|
||||
replacer := strings.NewReplacer(replacements...)
|
||||
body := replaceWithReplacer(c.Body, replacer)
|
||||
subject := replaceWithReplacer(c.Subject, replacer)
|
||||
|
@ -1825,7 +1964,7 @@ func executeFsRuleAction(c dataprovider.EventActionFilesystemConfig, conditions
|
|||
params *EventParams,
|
||||
) error {
|
||||
addObjectData := false
|
||||
replacements := params.getStringReplacements(addObjectData)
|
||||
replacements := params.getStringReplacements(addObjectData, false)
|
||||
replacer := strings.NewReplacer(replacements...)
|
||||
switch c.Type {
|
||||
case dataprovider.FilesystemActionRename:
|
||||
|
@ -2207,6 +2346,71 @@ func executePwdExpirationCheckRuleAction(config dataprovider.EventActionPassword
|
|||
return nil
|
||||
}
|
||||
|
||||
func executeAdminCheckAction(c *dataprovider.EventActionIDPAccountCheck, params *EventParams) (*dataprovider.Admin, error) {
|
||||
admin, err := dataprovider.AdminExists(params.Name)
|
||||
exists := err == nil
|
||||
if exists && c.Mode == 1 {
|
||||
return &admin, nil
|
||||
}
|
||||
if err != nil && !errors.Is(err, util.ErrNotFound) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
replacements := params.getStringReplacements(false, true)
|
||||
replacer := strings.NewReplacer(replacements...)
|
||||
data := replaceWithReplacer(c.TemplateAdmin, replacer)
|
||||
|
||||
var newAdmin dataprovider.Admin
|
||||
err = json.Unmarshal([]byte(data), &newAdmin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if newAdmin.Password == "" {
|
||||
newAdmin.Password = util.GenerateUniqueID()
|
||||
}
|
||||
if exists {
|
||||
eventManagerLog(logger.LevelDebug, "updating admin %q after IDP login", params.Name)
|
||||
err = dataprovider.UpdateAdmin(&newAdmin, dataprovider.ActionExecutorSystem, "", "")
|
||||
} else {
|
||||
eventManagerLog(logger.LevelDebug, "creating admin %q after IDP login", params.Name)
|
||||
err = dataprovider.AddAdmin(&newAdmin, dataprovider.ActionExecutorSystem, "", "")
|
||||
}
|
||||
return &newAdmin, err
|
||||
}
|
||||
|
||||
func executeUserCheckAction(c *dataprovider.EventActionIDPAccountCheck, params *EventParams) (*dataprovider.User, error) {
|
||||
user, err := dataprovider.UserExists(params.Name, "")
|
||||
exists := err == nil
|
||||
if exists && c.Mode == 1 {
|
||||
err = user.LoadAndApplyGroupSettings()
|
||||
return &user, err
|
||||
}
|
||||
if err != nil && !errors.Is(err, util.ErrNotFound) {
|
||||
return nil, err
|
||||
}
|
||||
replacements := params.getStringReplacements(false, true)
|
||||
replacer := strings.NewReplacer(replacements...)
|
||||
data := replaceWithReplacer(c.TemplateUser, replacer)
|
||||
|
||||
var newUser dataprovider.User
|
||||
err = json.Unmarshal([]byte(data), &newUser)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if exists {
|
||||
eventManagerLog(logger.LevelDebug, "updating user %q after IDP login", params.Name)
|
||||
err = dataprovider.UpdateUser(&newUser, dataprovider.ActionExecutorSystem, "", "")
|
||||
} else {
|
||||
eventManagerLog(logger.LevelDebug, "creating user %q after IDP login", params.Name)
|
||||
err = dataprovider.AddUser(&newUser, dataprovider.ActionExecutorSystem, "", "")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u, err := dataprovider.GetUserWithGroupSettings(params.Name, "")
|
||||
return &u, err
|
||||
}
|
||||
|
||||
func executeRuleAction(action dataprovider.BaseEventAction, params *EventParams,
|
||||
conditions dataprovider.ConditionOptions,
|
||||
) error {
|
||||
|
@ -2252,6 +2456,43 @@ func executeRuleAction(action dataprovider.BaseEventAction, params *EventParams,
|
|||
return err
|
||||
}
|
||||
|
||||
func executeIDPAccountCheckRule(rule dataprovider.EventRule, params EventParams) (*dataprovider.User,
|
||||
*dataprovider.Admin, error,
|
||||
) {
|
||||
for _, action := range rule.Actions {
|
||||
if action.Type == dataprovider.ActionTypeIDPAccountCheck {
|
||||
startTime := time.Now()
|
||||
var user *dataprovider.User
|
||||
var admin *dataprovider.Admin
|
||||
var err error
|
||||
var failedActions []string
|
||||
paramsCopy := params.getACopy()
|
||||
|
||||
switch params.Event {
|
||||
case IDPLoginAdmin:
|
||||
admin, err = executeAdminCheckAction(&action.BaseEventAction.Options.IDPConfig, paramsCopy)
|
||||
case IDPLoginUser:
|
||||
user, err = executeUserCheckAction(&action.BaseEventAction.Options.IDPConfig, paramsCopy)
|
||||
default:
|
||||
err = fmt.Errorf("unsupported IDP login event: %q", params.Event)
|
||||
}
|
||||
if err != nil {
|
||||
paramsCopy.AddError(fmt.Errorf("unable to handle %q: %w", params.Event, err))
|
||||
eventManagerLog(logger.LevelError, "unable to handle IDP login event %q, err: %v", params.Event, err)
|
||||
failedActions = append(failedActions, action.Name)
|
||||
} else {
|
||||
eventManagerLog(logger.LevelDebug, "executed action %q for rule %q, elapsed %s",
|
||||
action.Name, rule.Name, time.Since(startTime))
|
||||
}
|
||||
// execute async actions if any, including failure actions
|
||||
go executeRuleAsyncActions(rule, paramsCopy, failedActions)
|
||||
return user, admin, err
|
||||
}
|
||||
}
|
||||
eventManagerLog(logger.LevelError, "no action executed for IDP login event %q, event rule: %q", params.Event, rule.Name)
|
||||
return nil, nil, errors.New("no action executed")
|
||||
}
|
||||
|
||||
func executeSyncRulesActions(rules []dataprovider.EventRule, params EventParams) error {
|
||||
var errRes error
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ import (
|
|||
|
||||
func TestEventRuleMatch(t *testing.T) {
|
||||
role := "role1"
|
||||
conditions := dataprovider.EventConditions{
|
||||
conditions := &dataprovider.EventConditions{
|
||||
ProviderEvents: []string{"add", "update"},
|
||||
Options: dataprovider.ConditionOptions{
|
||||
Names: []dataprovider.ConditionPattern{
|
||||
|
@ -62,40 +62,40 @@ func TestEventRuleMatch(t *testing.T) {
|
|||
},
|
||||
},
|
||||
}
|
||||
res := eventManager.checkProviderEventMatch(conditions, EventParams{
|
||||
res := eventManager.checkProviderEventMatch(conditions, &EventParams{
|
||||
Name: "user1",
|
||||
Role: role,
|
||||
Event: "add",
|
||||
})
|
||||
assert.False(t, res)
|
||||
res = eventManager.checkProviderEventMatch(conditions, EventParams{
|
||||
res = eventManager.checkProviderEventMatch(conditions, &EventParams{
|
||||
Name: "user2",
|
||||
Role: role,
|
||||
Event: "update",
|
||||
})
|
||||
assert.True(t, res)
|
||||
res = eventManager.checkProviderEventMatch(conditions, EventParams{
|
||||
res = eventManager.checkProviderEventMatch(conditions, &EventParams{
|
||||
Name: "user2",
|
||||
Role: role,
|
||||
Event: "delete",
|
||||
})
|
||||
assert.False(t, res)
|
||||
conditions.Options.ProviderObjects = []string{"api_key"}
|
||||
res = eventManager.checkProviderEventMatch(conditions, EventParams{
|
||||
res = eventManager.checkProviderEventMatch(conditions, &EventParams{
|
||||
Name: "user2",
|
||||
Event: "update",
|
||||
Role: role,
|
||||
ObjectType: "share",
|
||||
})
|
||||
assert.False(t, res)
|
||||
res = eventManager.checkProviderEventMatch(conditions, EventParams{
|
||||
res = eventManager.checkProviderEventMatch(conditions, &EventParams{
|
||||
Name: "user2",
|
||||
Event: "update",
|
||||
Role: role,
|
||||
ObjectType: "api_key",
|
||||
})
|
||||
assert.True(t, res)
|
||||
res = eventManager.checkProviderEventMatch(conditions, EventParams{
|
||||
res = eventManager.checkProviderEventMatch(conditions, &EventParams{
|
||||
Name: "user2",
|
||||
Event: "update",
|
||||
Role: role + "1",
|
||||
|
@ -103,7 +103,7 @@ func TestEventRuleMatch(t *testing.T) {
|
|||
})
|
||||
assert.False(t, res)
|
||||
// now test fs events
|
||||
conditions = dataprovider.EventConditions{
|
||||
conditions = &dataprovider.EventConditions{
|
||||
FsEvents: []string{operationUpload, operationDownload},
|
||||
Options: dataprovider.ConditionOptions{
|
||||
Names: []dataprovider.ConditionPattern{
|
||||
|
@ -138,41 +138,41 @@ func TestEventRuleMatch(t *testing.T) {
|
|||
ObjectName: "path.txt",
|
||||
FileSize: 20,
|
||||
}
|
||||
res = eventManager.checkFsEventMatch(conditions, params)
|
||||
res = eventManager.checkFsEventMatch(conditions, ¶ms)
|
||||
assert.False(t, res)
|
||||
params.Event = operationDownload
|
||||
res = eventManager.checkFsEventMatch(conditions, params)
|
||||
res = eventManager.checkFsEventMatch(conditions, ¶ms)
|
||||
assert.True(t, res)
|
||||
params.Role = role
|
||||
res = eventManager.checkFsEventMatch(conditions, params)
|
||||
res = eventManager.checkFsEventMatch(conditions, ¶ms)
|
||||
assert.False(t, res)
|
||||
params.Role = ""
|
||||
params.Name = "name"
|
||||
res = eventManager.checkFsEventMatch(conditions, params)
|
||||
res = eventManager.checkFsEventMatch(conditions, ¶ms)
|
||||
assert.False(t, res)
|
||||
params.Name = "user5"
|
||||
res = eventManager.checkFsEventMatch(conditions, params)
|
||||
res = eventManager.checkFsEventMatch(conditions, ¶ms)
|
||||
assert.True(t, res)
|
||||
params.VirtualPath = "/sub/f.jpg"
|
||||
params.ObjectName = path.Base(params.VirtualPath)
|
||||
res = eventManager.checkFsEventMatch(conditions, params)
|
||||
res = eventManager.checkFsEventMatch(conditions, ¶ms)
|
||||
assert.False(t, res)
|
||||
params.VirtualPath = "/sub/f.txt"
|
||||
params.ObjectName = path.Base(params.VirtualPath)
|
||||
res = eventManager.checkFsEventMatch(conditions, params)
|
||||
res = eventManager.checkFsEventMatch(conditions, ¶ms)
|
||||
assert.True(t, res)
|
||||
params.Protocol = ProtocolHTTP
|
||||
res = eventManager.checkFsEventMatch(conditions, params)
|
||||
res = eventManager.checkFsEventMatch(conditions, ¶ms)
|
||||
assert.False(t, res)
|
||||
params.Protocol = ProtocolSFTP
|
||||
params.FileSize = 5
|
||||
res = eventManager.checkFsEventMatch(conditions, params)
|
||||
res = eventManager.checkFsEventMatch(conditions, ¶ms)
|
||||
assert.False(t, res)
|
||||
params.FileSize = 50
|
||||
res = eventManager.checkFsEventMatch(conditions, params)
|
||||
res = eventManager.checkFsEventMatch(conditions, ¶ms)
|
||||
assert.False(t, res)
|
||||
params.FileSize = 25
|
||||
res = eventManager.checkFsEventMatch(conditions, params)
|
||||
res = eventManager.checkFsEventMatch(conditions, ¶ms)
|
||||
assert.True(t, res)
|
||||
// bad pattern
|
||||
conditions.Options.Names = []dataprovider.ConditionPattern{
|
||||
|
@ -180,10 +180,10 @@ func TestEventRuleMatch(t *testing.T) {
|
|||
Pattern: "[-]",
|
||||
},
|
||||
}
|
||||
res = eventManager.checkFsEventMatch(conditions, params)
|
||||
res = eventManager.checkFsEventMatch(conditions, ¶ms)
|
||||
assert.False(t, res)
|
||||
// check fs events with group name filters
|
||||
conditions = dataprovider.EventConditions{
|
||||
conditions = &dataprovider.EventConditions{
|
||||
FsEvents: []string{operationUpload, operationDownload},
|
||||
Options: dataprovider.ConditionOptions{
|
||||
GroupNames: []dataprovider.ConditionPattern{
|
||||
|
@ -200,7 +200,7 @@ func TestEventRuleMatch(t *testing.T) {
|
|||
Name: "user1",
|
||||
Event: operationUpload,
|
||||
}
|
||||
res = eventManager.checkFsEventMatch(conditions, params)
|
||||
res = eventManager.checkFsEventMatch(conditions, ¶ms)
|
||||
assert.False(t, res)
|
||||
params.Groups = []sdk.GroupMapping{
|
||||
{
|
||||
|
@ -212,7 +212,7 @@ func TestEventRuleMatch(t *testing.T) {
|
|||
Type: sdk.GroupTypeSecondary,
|
||||
},
|
||||
}
|
||||
res = eventManager.checkFsEventMatch(conditions, params)
|
||||
res = eventManager.checkFsEventMatch(conditions, ¶ms)
|
||||
assert.False(t, res)
|
||||
params.Groups = []sdk.GroupMapping{
|
||||
{
|
||||
|
@ -224,7 +224,7 @@ func TestEventRuleMatch(t *testing.T) {
|
|||
Type: sdk.GroupTypeSecondary,
|
||||
},
|
||||
}
|
||||
res = eventManager.checkFsEventMatch(conditions, params)
|
||||
res = eventManager.checkFsEventMatch(conditions, ¶ms)
|
||||
assert.True(t, res)
|
||||
// check user conditions
|
||||
user := dataprovider.User{}
|
||||
|
@ -269,6 +269,58 @@ func TestEventRuleMatch(t *testing.T) {
|
|||
},
|
||||
})
|
||||
assert.False(t, res)
|
||||
res = eventManager.checkIPDLoginEventMatch(&dataprovider.EventConditions{
|
||||
IDPLoginEvent: 0,
|
||||
}, &EventParams{
|
||||
Event: IDPLoginAdmin,
|
||||
})
|
||||
assert.True(t, res)
|
||||
res = eventManager.checkIPDLoginEventMatch(&dataprovider.EventConditions{
|
||||
IDPLoginEvent: 2,
|
||||
}, &EventParams{
|
||||
Event: IDPLoginAdmin,
|
||||
})
|
||||
assert.True(t, res)
|
||||
res = eventManager.checkIPDLoginEventMatch(&dataprovider.EventConditions{
|
||||
IDPLoginEvent: 1,
|
||||
}, &EventParams{
|
||||
Event: IDPLoginAdmin,
|
||||
})
|
||||
assert.False(t, res)
|
||||
res = eventManager.checkIPDLoginEventMatch(&dataprovider.EventConditions{
|
||||
IDPLoginEvent: 1,
|
||||
}, &EventParams{
|
||||
Event: IDPLoginUser,
|
||||
})
|
||||
assert.True(t, res)
|
||||
res = eventManager.checkIPDLoginEventMatch(&dataprovider.EventConditions{
|
||||
IDPLoginEvent: 1,
|
||||
}, &EventParams{
|
||||
Name: "user",
|
||||
Event: IDPLoginUser,
|
||||
})
|
||||
assert.True(t, res)
|
||||
res = eventManager.checkIPDLoginEventMatch(&dataprovider.EventConditions{
|
||||
IDPLoginEvent: 1,
|
||||
Options: dataprovider.ConditionOptions{
|
||||
Names: []dataprovider.ConditionPattern{
|
||||
{
|
||||
Pattern: "abc",
|
||||
},
|
||||
},
|
||||
},
|
||||
}, &EventParams{
|
||||
Name: "user",
|
||||
Event: IDPLoginUser,
|
||||
})
|
||||
assert.False(t, res)
|
||||
res = eventManager.checkIPDLoginEventMatch(&dataprovider.EventConditions{
|
||||
IDPLoginEvent: 2,
|
||||
}, &EventParams{
|
||||
Name: "user",
|
||||
Event: IDPLoginUser,
|
||||
})
|
||||
assert.False(t, res)
|
||||
}
|
||||
|
||||
func TestDoubleStarMatching(t *testing.T) {
|
||||
|
@ -453,6 +505,10 @@ func TestEventManagerErrors(t *testing.T) {
|
|||
err = executePwdExpirationCheckRuleAction(dataprovider.EventActionPasswordExpiration{},
|
||||
dataprovider.ConditionOptions{}, &EventParams{})
|
||||
assert.Error(t, err)
|
||||
_, err = executeAdminCheckAction(&dataprovider.EventActionIDPAccountCheck{}, &EventParams{})
|
||||
assert.Error(t, err)
|
||||
_, err = executeUserCheckAction(&dataprovider.EventActionIDPAccountCheck{}, &EventParams{})
|
||||
assert.Error(t, err)
|
||||
|
||||
groupName := "agroup"
|
||||
err = executeQuotaResetForUser(&dataprovider.User{
|
||||
|
@ -545,7 +601,7 @@ func TestEventManagerErrors(t *testing.T) {
|
|||
}}, dataprovider.EventActionPasswordExpiration{})
|
||||
assert.Error(t, err)
|
||||
|
||||
_, _, err = getHTTPRuleActionBody(dataprovider.EventActionHTTPConfig{
|
||||
_, _, err = getHTTPRuleActionBody(&dataprovider.EventActionHTTPConfig{
|
||||
Method: http.MethodPost,
|
||||
Parts: []dataprovider.HTTPPart{
|
||||
{
|
||||
|
@ -562,7 +618,7 @@ func TestEventManagerErrors(t *testing.T) {
|
|||
Type: sdk.GroupTypePrimary,
|
||||
},
|
||||
},
|
||||
}, &EventParams{})
|
||||
}, &EventParams{}, false)
|
||||
assert.Error(t, err)
|
||||
|
||||
dataRetentionAction := dataprovider.BaseEventAction{
|
||||
|
@ -1181,11 +1237,17 @@ func TestEventRuleActions(t *testing.T) {
|
|||
assert.Contains(t, err.Error(), "no folder quota reset executed")
|
||||
}
|
||||
|
||||
body, _, err := getHTTPRuleActionBody(dataprovider.EventActionHTTPConfig{
|
||||
body, _, err := getHTTPRuleActionBody(&dataprovider.EventActionHTTPConfig{
|
||||
Method: http.MethodPost,
|
||||
}, nil, nil, dataprovider.User{}, &EventParams{})
|
||||
}, nil, nil, dataprovider.User{}, &EventParams{}, true)
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, body)
|
||||
body, _, err = getHTTPRuleActionBody(&dataprovider.EventActionHTTPConfig{
|
||||
Method: http.MethodPost,
|
||||
Body: "test body",
|
||||
}, nil, nil, dataprovider.User{}, &EventParams{}, false)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, body)
|
||||
|
||||
err = os.RemoveAll(folder1.MappedPath)
|
||||
assert.NoError(t, err)
|
||||
|
@ -1195,6 +1257,99 @@ func TestEventRuleActions(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestIDPAccountCheckRule(t *testing.T) {
|
||||
_, _, err := executeIDPAccountCheckRule(dataprovider.EventRule{}, EventParams{})
|
||||
if assert.Error(t, err) {
|
||||
assert.Contains(t, err.Error(), "no action executed")
|
||||
}
|
||||
_, _, err = executeIDPAccountCheckRule(dataprovider.EventRule{
|
||||
Actions: []dataprovider.EventAction{
|
||||
{
|
||||
BaseEventAction: dataprovider.BaseEventAction{
|
||||
Name: "n",
|
||||
Type: dataprovider.ActionTypeIDPAccountCheck,
|
||||
},
|
||||
},
|
||||
},
|
||||
}, EventParams{Event: "invalid"})
|
||||
if assert.Error(t, err) {
|
||||
assert.Contains(t, err.Error(), "unsupported IDP login event")
|
||||
}
|
||||
// invalid json
|
||||
_, err = executeAdminCheckAction(&dataprovider.EventActionIDPAccountCheck{TemplateAdmin: "{"}, &EventParams{Name: "missing admin"})
|
||||
assert.Error(t, err)
|
||||
_, err = executeUserCheckAction(&dataprovider.EventActionIDPAccountCheck{TemplateUser: "["}, &EventParams{Name: "missing user"})
|
||||
assert.Error(t, err)
|
||||
_, err = executeUserCheckAction(&dataprovider.EventActionIDPAccountCheck{TemplateUser: "{}"}, &EventParams{Name: "invalid user template"})
|
||||
assert.ErrorIs(t, err, util.ErrValidation)
|
||||
username := "u"
|
||||
c := &dataprovider.EventActionIDPAccountCheck{
|
||||
Mode: 1,
|
||||
TemplateUser: `{"username":"` + username + `","status":1,"home_dir":"` + util.JSONEscape(filepath.Join(os.TempDir())) + `","permissions":{"/":["*"]}}`,
|
||||
}
|
||||
params := &EventParams{
|
||||
Name: username,
|
||||
Event: IDPLoginUser,
|
||||
}
|
||||
user, err := executeUserCheckAction(c, params)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, username, user.Username)
|
||||
assert.Equal(t, 1, user.Status)
|
||||
user.Status = 0
|
||||
err = dataprovider.UpdateUser(user, "", "", "")
|
||||
assert.NoError(t, err)
|
||||
// the user is not changed
|
||||
user, err = executeUserCheckAction(c, params)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, username, user.Username)
|
||||
assert.Equal(t, 0, user.Status)
|
||||
// change the mode, the user is now updated
|
||||
c.Mode = 0
|
||||
user, err = executeUserCheckAction(c, params)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, username, user.Username)
|
||||
assert.Equal(t, 1, user.Status)
|
||||
|
||||
err = dataprovider.DeleteUser(username, "", "", "")
|
||||
assert.NoError(t, err)
|
||||
// check rule consistency
|
||||
r := dataprovider.EventRule{
|
||||
Actions: []dataprovider.EventAction{
|
||||
{
|
||||
BaseEventAction: dataprovider.BaseEventAction{
|
||||
Type: dataprovider.ActionTypeIDPAccountCheck,
|
||||
},
|
||||
Order: 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
err = r.CheckActionsConsistency("")
|
||||
if assert.Error(t, err) {
|
||||
assert.Contains(t, err.Error(), "IDP account check action is only supported for IDP login trigger")
|
||||
}
|
||||
r.Trigger = dataprovider.EventTriggerIDPLogin
|
||||
err = r.CheckActionsConsistency("")
|
||||
if assert.Error(t, err) {
|
||||
assert.Contains(t, err.Error(), "IDP account check must be a sync action")
|
||||
}
|
||||
r.Actions[0].Options.ExecuteSync = true
|
||||
err = r.CheckActionsConsistency("")
|
||||
assert.NoError(t, err)
|
||||
r.Actions = append(r.Actions, dataprovider.EventAction{
|
||||
BaseEventAction: dataprovider.BaseEventAction{
|
||||
Type: dataprovider.ActionTypeCommand,
|
||||
},
|
||||
Options: dataprovider.EventActionOptions{
|
||||
ExecuteSync: true,
|
||||
},
|
||||
Order: 2,
|
||||
})
|
||||
err = r.CheckActionsConsistency("")
|
||||
if assert.Error(t, err) {
|
||||
assert.Contains(t, err.Error(), "IDP account check must be the only sync action")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserExpirationCheck(t *testing.T) {
|
||||
username := "test_user_expiration_check"
|
||||
user := dataprovider.User{
|
||||
|
@ -1778,6 +1933,22 @@ func TestEventParamsCopy(t *testing.T) {
|
|||
assert.Equal(t, "a_copy", paramsCopy.retentionChecks[0].ActionName)
|
||||
assert.Equal(t, "p_copy", paramsCopy.retentionChecks[0].Results[0].Path)
|
||||
assert.Equal(t, 2, paramsCopy.retentionChecks[0].Results[0].Retention)
|
||||
assert.Nil(t, params.IDPCustomFields)
|
||||
params.addIDPCustomFields(nil)
|
||||
assert.Nil(t, params.IDPCustomFields)
|
||||
params.IDPCustomFields = &map[string]string{
|
||||
"field1": "val1",
|
||||
}
|
||||
paramsCopy = params.getACopy()
|
||||
for k, v := range *paramsCopy.IDPCustomFields {
|
||||
assert.Equal(t, "field1", k)
|
||||
assert.Equal(t, "val1", v)
|
||||
}
|
||||
assert.Equal(t, params.IDPCustomFields, paramsCopy.IDPCustomFields)
|
||||
paramsCopy.addIDPCustomFields(&map[string]any{
|
||||
"field2": "val2",
|
||||
})
|
||||
assert.NotEqual(t, params.IDPCustomFields, paramsCopy.IDPCustomFields)
|
||||
}
|
||||
|
||||
func TestEventParamsStatusFromError(t *testing.T) {
|
||||
|
@ -1810,14 +1981,14 @@ func TestWriteHTTPPartsError(t *testing.T) {
|
|||
errTest: io.ErrShortWrite,
|
||||
})
|
||||
|
||||
err := writeHTTPPart(m, dataprovider.HTTPPart{}, nil, nil, nil, &EventParams{})
|
||||
err := writeHTTPPart(m, dataprovider.HTTPPart{}, nil, nil, nil, &EventParams{}, false)
|
||||
assert.ErrorIs(t, err, io.ErrShortWrite)
|
||||
|
||||
body := "test body"
|
||||
m = multipart.NewWriter(&testWriter{sentinel: body})
|
||||
err = writeHTTPPart(m, dataprovider.HTTPPart{
|
||||
Body: body,
|
||||
}, nil, nil, nil, &EventParams{})
|
||||
}, nil, nil, nil, &EventParams{}, false)
|
||||
assert.ErrorIs(t, err, io.ErrUnexpectedEOF)
|
||||
}
|
||||
|
||||
|
|
|
@ -6033,6 +6033,286 @@ func TestEventRuleRenameEvent(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestEventRuleIDPLogin(t *testing.T) {
|
||||
smtpCfg := smtp.Config{
|
||||
Host: "127.0.0.1",
|
||||
Port: 2525,
|
||||
From: "notify@example.com",
|
||||
TemplatesPath: "templates",
|
||||
}
|
||||
err := smtpCfg.Initialize(configDir, true)
|
||||
require.NoError(t, err)
|
||||
lastReceivedEmail.reset()
|
||||
|
||||
username := `test_"idp_"login`
|
||||
custom1 := `cust"oa"1`
|
||||
u := map[string]any{
|
||||
"username": "{{Name}}",
|
||||
"status": 1,
|
||||
"home_dir": filepath.Join(os.TempDir(), "{{IDPFieldcustom1}}"),
|
||||
"permissions": map[string][]string{
|
||||
"/": {dataprovider.PermAny},
|
||||
},
|
||||
}
|
||||
userTmpl, err := json.Marshal(u)
|
||||
require.NoError(t, err)
|
||||
a := map[string]any{
|
||||
"username": "{{Name}}",
|
||||
"status": 1,
|
||||
"permissions": []string{dataprovider.PermAdminAny},
|
||||
}
|
||||
adminTmpl, err := json.Marshal(a)
|
||||
require.NoError(t, err)
|
||||
|
||||
a1 := dataprovider.BaseEventAction{
|
||||
Name: "a1",
|
||||
Type: dataprovider.ActionTypeIDPAccountCheck,
|
||||
Options: dataprovider.BaseEventActionOptions{
|
||||
IDPConfig: dataprovider.EventActionIDPAccountCheck{
|
||||
Mode: 1, // create if not exists
|
||||
TemplateUser: string(userTmpl),
|
||||
TemplateAdmin: string(adminTmpl),
|
||||
},
|
||||
},
|
||||
}
|
||||
action1, _, err := httpdtest.AddEventAction(a1, http.StatusCreated)
|
||||
assert.NoError(t, err)
|
||||
a2 := dataprovider.BaseEventAction{
|
||||
Name: "a2",
|
||||
Type: dataprovider.ActionTypeEmail,
|
||||
Options: dataprovider.BaseEventActionOptions{
|
||||
EmailConfig: dataprovider.EventActionEmailConfig{
|
||||
Recipients: []string{"test@example.com"},
|
||||
Subject: `"{{Event}} {{StatusString}}"`,
|
||||
Body: "{{Name}} Custom field: {{IDPFieldcustom1}}",
|
||||
},
|
||||
},
|
||||
}
|
||||
action2, _, err := httpdtest.AddEventAction(a2, http.StatusCreated)
|
||||
assert.NoError(t, err)
|
||||
r1 := dataprovider.EventRule{
|
||||
Name: "test rule IDP login",
|
||||
Status: 1,
|
||||
Trigger: dataprovider.EventTriggerIDPLogin,
|
||||
Conditions: dataprovider.EventConditions{
|
||||
IDPLoginEvent: dataprovider.IDPLoginUser,
|
||||
},
|
||||
Actions: []dataprovider.EventAction{
|
||||
{
|
||||
BaseEventAction: dataprovider.BaseEventAction{
|
||||
Name: action1.Name, // the rule is not sync and will be skipped
|
||||
},
|
||||
Order: 1,
|
||||
},
|
||||
{
|
||||
BaseEventAction: dataprovider.BaseEventAction{
|
||||
Name: action2.Name,
|
||||
},
|
||||
Order: 2,
|
||||
},
|
||||
},
|
||||
}
|
||||
rule1, resp, err := httpdtest.AddEventRule(r1, http.StatusCreated)
|
||||
assert.NoError(t, err, string(resp))
|
||||
|
||||
customFields := map[string]any{
|
||||
"custom1": custom1,
|
||||
}
|
||||
user, admin, err := common.HandleIDPLoginEvent(common.EventParams{
|
||||
Name: username,
|
||||
Event: common.IDPLoginUser,
|
||||
Status: 1,
|
||||
}, &customFields)
|
||||
assert.Nil(t, user)
|
||||
assert.Nil(t, admin)
|
||||
assert.NoError(t, err)
|
||||
|
||||
rule1.Actions[0].Options.ExecuteSync = true
|
||||
rule1, resp, err = httpdtest.UpdateEventRule(rule1, http.StatusOK)
|
||||
assert.NoError(t, err, string(resp))
|
||||
user, admin, err = common.HandleIDPLoginEvent(common.EventParams{
|
||||
Name: username,
|
||||
Event: common.IDPLoginUser,
|
||||
Status: 1,
|
||||
}, &customFields)
|
||||
if assert.NotNil(t, user) {
|
||||
assert.Equal(t, filepath.Join(os.TempDir(), custom1), user.GetHomeDir())
|
||||
_, err = httpdtest.RemoveUser(*user, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
assert.Nil(t, admin)
|
||||
assert.NoError(t, err)
|
||||
assert.Eventually(t, func() bool {
|
||||
return lastReceivedEmail.get().From != ""
|
||||
}, 3000*time.Millisecond, 100*time.Millisecond)
|
||||
email := lastReceivedEmail.get()
|
||||
assert.Len(t, email.To, 1)
|
||||
assert.True(t, util.Contains(email.To, "test@example.com"))
|
||||
assert.Contains(t, email.Data, fmt.Sprintf(`Subject: "%s OK"`, common.IDPLoginUser))
|
||||
assert.Contains(t, email.Data, username)
|
||||
assert.Contains(t, email.Data, custom1)
|
||||
|
||||
user, admin, err = common.HandleIDPLoginEvent(common.EventParams{
|
||||
Name: username,
|
||||
Event: common.IDPLoginAdmin,
|
||||
Status: 1,
|
||||
}, &customFields)
|
||||
assert.Nil(t, user)
|
||||
assert.Nil(t, admin)
|
||||
assert.NoError(t, err)
|
||||
|
||||
rule1.Conditions.IDPLoginEvent = dataprovider.IDPLoginAny
|
||||
rule1.Actions = []dataprovider.EventAction{
|
||||
{
|
||||
BaseEventAction: dataprovider.BaseEventAction{
|
||||
Name: action1.Name,
|
||||
},
|
||||
Options: dataprovider.EventActionOptions{
|
||||
ExecuteSync: true,
|
||||
},
|
||||
Order: 1,
|
||||
},
|
||||
}
|
||||
rule1, _, err = httpdtest.UpdateEventRule(rule1, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
|
||||
r2 := dataprovider.EventRule{
|
||||
Name: "test email on IDP login",
|
||||
Status: 1,
|
||||
Trigger: dataprovider.EventTriggerIDPLogin,
|
||||
Conditions: dataprovider.EventConditions{
|
||||
IDPLoginEvent: dataprovider.IDPLoginAdmin,
|
||||
},
|
||||
Actions: []dataprovider.EventAction{
|
||||
{
|
||||
BaseEventAction: dataprovider.BaseEventAction{
|
||||
Name: action2.Name,
|
||||
},
|
||||
Order: 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
rule2, resp, err := httpdtest.AddEventRule(r2, http.StatusCreated)
|
||||
assert.NoError(t, err, string(resp))
|
||||
|
||||
lastReceivedEmail.reset()
|
||||
user, admin, err = common.HandleIDPLoginEvent(common.EventParams{
|
||||
Name: username,
|
||||
Event: common.IDPLoginAdmin,
|
||||
Status: 1,
|
||||
}, &customFields)
|
||||
assert.Nil(t, user)
|
||||
if assert.NotNil(t, admin) {
|
||||
assert.Equal(t, 1, admin.Status)
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
assert.Eventually(t, func() bool {
|
||||
return lastReceivedEmail.get().From != ""
|
||||
}, 3000*time.Millisecond, 100*time.Millisecond)
|
||||
email = lastReceivedEmail.get()
|
||||
assert.Len(t, email.To, 1)
|
||||
assert.True(t, util.Contains(email.To, "test@example.com"))
|
||||
assert.Contains(t, email.Data, fmt.Sprintf(`Subject: "%s OK"`, common.IDPLoginAdmin))
|
||||
assert.Contains(t, email.Data, username)
|
||||
assert.Contains(t, email.Data, custom1)
|
||||
admin.Status = 0
|
||||
_, _, err = httpdtest.UpdateAdmin(*admin, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
user, admin, err = common.HandleIDPLoginEvent(common.EventParams{
|
||||
Name: username,
|
||||
Event: common.IDPLoginAdmin,
|
||||
Status: 1,
|
||||
}, &customFields)
|
||||
assert.Nil(t, user)
|
||||
if assert.NotNil(t, admin) {
|
||||
assert.Equal(t, 0, admin.Status)
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
action1.Options.IDPConfig.Mode = 0
|
||||
action1, _, err = httpdtest.UpdateEventAction(action1, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
user, admin, err = common.HandleIDPLoginEvent(common.EventParams{
|
||||
Name: username,
|
||||
Event: common.IDPLoginAdmin,
|
||||
Status: 1,
|
||||
}, &customFields)
|
||||
assert.Nil(t, user)
|
||||
if assert.NotNil(t, admin) {
|
||||
assert.Equal(t, 1, admin.Status)
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
_, err = httpdtest.RemoveAdmin(*admin, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
|
||||
r3 := dataprovider.EventRule{
|
||||
Name: "test rule2 IDP login",
|
||||
Status: 1,
|
||||
Trigger: dataprovider.EventTriggerIDPLogin,
|
||||
Conditions: dataprovider.EventConditions{
|
||||
IDPLoginEvent: dataprovider.IDPLoginAny,
|
||||
},
|
||||
Actions: []dataprovider.EventAction{
|
||||
{
|
||||
BaseEventAction: dataprovider.BaseEventAction{
|
||||
Name: action1.Name,
|
||||
},
|
||||
Order: 1,
|
||||
Options: dataprovider.EventActionOptions{
|
||||
ExecuteSync: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
rule3, resp, err := httpdtest.AddEventRule(r3, http.StatusCreated)
|
||||
assert.NoError(t, err, string(resp))
|
||||
user, admin, err = common.HandleIDPLoginEvent(common.EventParams{
|
||||
Name: username,
|
||||
Event: common.IDPLoginAdmin,
|
||||
Status: 1,
|
||||
}, &customFields)
|
||||
assert.Nil(t, user)
|
||||
assert.Nil(t, admin)
|
||||
if assert.Error(t, err) {
|
||||
assert.Contains(t, err.Error(), "more than one account check action rules matches")
|
||||
}
|
||||
|
||||
_, err = httpdtest.RemoveEventRule(rule3, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
|
||||
action1.Options.IDPConfig.TemplateAdmin = `{}`
|
||||
action1, _, err = httpdtest.UpdateEventAction(action1, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
_, _, err = common.HandleIDPLoginEvent(common.EventParams{
|
||||
Name: username,
|
||||
Event: common.IDPLoginAdmin,
|
||||
Status: 1,
|
||||
}, &customFields)
|
||||
assert.ErrorIs(t, err, util.ErrValidation)
|
||||
|
||||
_, err = httpdtest.RemoveEventRule(rule1, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
|
||||
user, admin, err = common.HandleIDPLoginEvent(common.EventParams{
|
||||
Name: username,
|
||||
Event: common.IDPLoginAdmin,
|
||||
Status: 1,
|
||||
}, &customFields)
|
||||
assert.Nil(t, user)
|
||||
assert.Nil(t, admin)
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, err = httpdtest.RemoveEventRule(rule2, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
_, err = httpdtest.RemoveEventAction(action1, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
_, err = httpdtest.RemoveEventAction(action2, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
|
||||
smtpCfg = smtp.Config{}
|
||||
err = smtpCfg.Initialize(configDir, true)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestEventRuleCertificate(t *testing.T) {
|
||||
smtpCfg := smtp.Config{
|
||||
Host: "127.0.0.1",
|
||||
|
|
|
@ -729,7 +729,7 @@ func (p *BoltProvider) updateUser(user *User) error {
|
|||
})
|
||||
}
|
||||
|
||||
func (p *BoltProvider) deleteUser(user User, softDelete bool) error {
|
||||
func (p *BoltProvider) deleteUser(user User, _ bool) error {
|
||||
return p.dbHandle.Update(func(tx *bolt.Tx) error {
|
||||
bucket, err := p.getUsersBucket(tx)
|
||||
if err != nil {
|
||||
|
@ -1031,7 +1031,7 @@ func (p *BoltProvider) dumpFolders() ([]vfs.BaseVirtualFolder, error) {
|
|||
return folders, err
|
||||
}
|
||||
|
||||
func (p *BoltProvider) getFolders(limit, offset int, order string, minimal bool) ([]vfs.BaseVirtualFolder, error) {
|
||||
func (p *BoltProvider) getFolders(limit, offset int, order string, _ bool) ([]vfs.BaseVirtualFolder, error) {
|
||||
folders := make([]vfs.BaseVirtualFolder, 0, limit)
|
||||
var err error
|
||||
if limit <= 0 {
|
||||
|
@ -1279,7 +1279,7 @@ func (p *BoltProvider) getUsedFolderQuota(name string) (int, int64, error) {
|
|||
return folder.UsedQuotaFiles, folder.UsedQuotaSize, err
|
||||
}
|
||||
|
||||
func (p *BoltProvider) getGroups(limit, offset int, order string, minimal bool) ([]Group, error) {
|
||||
func (p *BoltProvider) getGroups(limit, offset int, order string, _ bool) ([]Group, error) {
|
||||
groups := make([]Group, 0, limit)
|
||||
var err error
|
||||
if limit <= 0 {
|
||||
|
@ -2001,75 +2001,75 @@ func (p *BoltProvider) updateShareLastUse(shareID string, numTokens int) error {
|
|||
})
|
||||
}
|
||||
|
||||
func (p *BoltProvider) getDefenderHosts(from int64, limit int) ([]DefenderEntry, error) {
|
||||
func (p *BoltProvider) getDefenderHosts(_ int64, _ int) ([]DefenderEntry, error) {
|
||||
return nil, ErrNotImplemented
|
||||
}
|
||||
|
||||
func (p *BoltProvider) getDefenderHostByIP(ip string, from int64) (DefenderEntry, error) {
|
||||
func (p *BoltProvider) getDefenderHostByIP(_ string, _ int64) (DefenderEntry, error) {
|
||||
return DefenderEntry{}, ErrNotImplemented
|
||||
}
|
||||
|
||||
func (p *BoltProvider) isDefenderHostBanned(ip string) (DefenderEntry, error) {
|
||||
func (p *BoltProvider) isDefenderHostBanned(_ string) (DefenderEntry, error) {
|
||||
return DefenderEntry{}, ErrNotImplemented
|
||||
}
|
||||
|
||||
func (p *BoltProvider) updateDefenderBanTime(ip string, minutes int) error {
|
||||
func (p *BoltProvider) updateDefenderBanTime(_ string, _ int) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func (p *BoltProvider) deleteDefenderHost(ip string) error {
|
||||
func (p *BoltProvider) deleteDefenderHost(_ string) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func (p *BoltProvider) addDefenderEvent(ip string, score int) error {
|
||||
func (p *BoltProvider) addDefenderEvent(_ string, _ int) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func (p *BoltProvider) setDefenderBanTime(ip string, banTime int64) error {
|
||||
func (p *BoltProvider) setDefenderBanTime(_ string, _ int64) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func (p *BoltProvider) cleanupDefender(from int64) error {
|
||||
func (p *BoltProvider) cleanupDefender(_ int64) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func (p *BoltProvider) addActiveTransfer(transfer ActiveTransfer) error {
|
||||
func (p *BoltProvider) addActiveTransfer(_ ActiveTransfer) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func (p *BoltProvider) updateActiveTransferSizes(ulSize, dlSize, transferID int64, connectionID string) error {
|
||||
func (p *BoltProvider) updateActiveTransferSizes(_, _, _ int64, _ string) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func (p *BoltProvider) removeActiveTransfer(transferID int64, connectionID string) error {
|
||||
func (p *BoltProvider) removeActiveTransfer(_ int64, _ string) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func (p *BoltProvider) cleanupActiveTransfers(before time.Time) error {
|
||||
func (p *BoltProvider) cleanupActiveTransfers(_ time.Time) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func (p *BoltProvider) getActiveTransfers(from time.Time) ([]ActiveTransfer, error) {
|
||||
func (p *BoltProvider) getActiveTransfers(_ time.Time) ([]ActiveTransfer, error) {
|
||||
return nil, ErrNotImplemented
|
||||
}
|
||||
|
||||
func (p *BoltProvider) addSharedSession(session Session) error {
|
||||
func (p *BoltProvider) addSharedSession(_ Session) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func (p *BoltProvider) deleteSharedSession(key string) error {
|
||||
func (p *BoltProvider) deleteSharedSession(_ string) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func (p *BoltProvider) getSharedSession(key string) (Session, error) {
|
||||
func (p *BoltProvider) getSharedSession(_ string) (Session, error) {
|
||||
return Session{}, ErrNotImplemented
|
||||
}
|
||||
|
||||
func (p *BoltProvider) cleanupSharedSessions(sessionType SessionType, before int64) error {
|
||||
func (p *BoltProvider) cleanupSharedSessions(_ SessionType, _ int64) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func (p *BoltProvider) getEventActions(limit, offset int, order string, minimal bool) ([]BaseEventAction, error) {
|
||||
func (p *BoltProvider) getEventActions(limit, offset int, order string, _ bool) ([]BaseEventAction, error) {
|
||||
if limit <= 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
@ -2508,7 +2508,7 @@ func (p *BoltProvider) updateEventRule(rule *EventRule) error {
|
|||
})
|
||||
}
|
||||
|
||||
func (p *BoltProvider) deleteEventRule(rule EventRule, softDelete bool) error {
|
||||
func (p *BoltProvider) deleteEventRule(rule EventRule, _ bool) error {
|
||||
return p.dbHandle.Update(func(tx *bolt.Tx) error {
|
||||
bucket, err := p.getRulesBucket(tx)
|
||||
if err != nil {
|
||||
|
@ -2537,19 +2537,19 @@ func (p *BoltProvider) deleteEventRule(rule EventRule, softDelete bool) error {
|
|||
})
|
||||
}
|
||||
|
||||
func (*BoltProvider) getTaskByName(name string) (Task, error) {
|
||||
func (*BoltProvider) getTaskByName(_ string) (Task, error) {
|
||||
return Task{}, ErrNotImplemented
|
||||
}
|
||||
|
||||
func (*BoltProvider) addTask(name string) error {
|
||||
func (*BoltProvider) addTask(_ string) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func (*BoltProvider) updateTask(name string, version int64) error {
|
||||
func (*BoltProvider) updateTask(_ string, _ int64) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func (*BoltProvider) updateTaskTimestamp(name string) error {
|
||||
func (*BoltProvider) updateTaskTimestamp(_ string) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
|
@ -2557,7 +2557,7 @@ func (*BoltProvider) addNode() error {
|
|||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func (*BoltProvider) getNodeByName(name string) (Node, error) {
|
||||
func (*BoltProvider) getNodeByName(_ string) (Node, error) {
|
||||
return Node{}, ErrNotImplemented
|
||||
}
|
||||
|
||||
|
@ -2683,7 +2683,7 @@ func (p *BoltProvider) deleteRole(role Role) error {
|
|||
})
|
||||
}
|
||||
|
||||
func (p *BoltProvider) getRoles(limit int, offset int, order string, minimal bool) ([]Role, error) {
|
||||
func (p *BoltProvider) getRoles(limit int, offset int, order string, _ bool) ([]Role, error) {
|
||||
roles := make([]Role, 0, limit)
|
||||
if limit <= 0 {
|
||||
return roles, nil
|
||||
|
@ -2827,7 +2827,7 @@ func (p *BoltProvider) updateIPListEntry(entry *IPListEntry) error {
|
|||
})
|
||||
}
|
||||
|
||||
func (p *BoltProvider) deleteIPListEntry(entry IPListEntry, softDelete bool) error {
|
||||
func (p *BoltProvider) deleteIPListEntry(entry IPListEntry, _ bool) error {
|
||||
return p.dbHandle.Update(func(tx *bolt.Tx) error {
|
||||
bucket, err := p.getIPListsBucket(tx)
|
||||
if err != nil {
|
||||
|
@ -2888,7 +2888,7 @@ func (p *BoltProvider) getIPListEntries(listType IPListType, filter, from, order
|
|||
return entries, err
|
||||
}
|
||||
|
||||
func (p *BoltProvider) getRecentlyUpdatedIPListEntries(after int64) ([]IPListEntry, error) {
|
||||
func (p *BoltProvider) getRecentlyUpdatedIPListEntries(_ int64) ([]IPListEntry, error) {
|
||||
return nil, ErrNotImplemented
|
||||
}
|
||||
|
||||
|
|
|
@ -47,13 +47,14 @@ const (
|
|||
ActionTypeMetadataCheck
|
||||
ActionTypePasswordExpirationCheck
|
||||
ActionTypeUserExpirationCheck
|
||||
ActionTypeIDPAccountCheck
|
||||
)
|
||||
|
||||
var (
|
||||
supportedEventActions = []int{ActionTypeHTTP, ActionTypeCommand, ActionTypeEmail, ActionTypeFilesystem,
|
||||
ActionTypeBackup, ActionTypeUserQuotaReset, ActionTypeFolderQuotaReset, ActionTypeTransferQuotaReset,
|
||||
ActionTypeDataRetentionCheck, ActionTypeMetadataCheck, ActionTypePasswordExpirationCheck,
|
||||
ActionTypeUserExpirationCheck}
|
||||
ActionTypeUserExpirationCheck, ActionTypeIDPAccountCheck}
|
||||
)
|
||||
|
||||
func isActionTypeValid(action int) bool {
|
||||
|
@ -84,6 +85,8 @@ func getActionTypeAsString(action int) string {
|
|||
return "Password expiration check"
|
||||
case ActionTypeUserExpirationCheck:
|
||||
return "User expiration check"
|
||||
case ActionTypeIDPAccountCheck:
|
||||
return "Identity Provider account check"
|
||||
default:
|
||||
return "Command"
|
||||
}
|
||||
|
@ -99,11 +102,12 @@ const (
|
|||
EventTriggerIPBlocked
|
||||
EventTriggerCertificate
|
||||
EventTriggerOnDemand
|
||||
EventTriggerIDPLogin
|
||||
)
|
||||
|
||||
var (
|
||||
supportedEventTriggers = []int{EventTriggerFsEvent, EventTriggerProviderEvent, EventTriggerSchedule,
|
||||
EventTriggerIPBlocked, EventTriggerCertificate, EventTriggerOnDemand}
|
||||
EventTriggerIPBlocked, EventTriggerCertificate, EventTriggerIDPLogin, EventTriggerOnDemand}
|
||||
)
|
||||
|
||||
func isEventTriggerValid(trigger int) bool {
|
||||
|
@ -122,11 +126,24 @@ func getTriggerTypeAsString(trigger int) string {
|
|||
return "Certificate renewal"
|
||||
case EventTriggerOnDemand:
|
||||
return "On demand"
|
||||
case EventTriggerIDPLogin:
|
||||
return "Identity Provider login"
|
||||
default:
|
||||
return "Schedule"
|
||||
}
|
||||
}
|
||||
|
||||
// Supported IDP login events
|
||||
const (
|
||||
IDPLoginAny = iota
|
||||
IDPLoginUser
|
||||
IDPLoginAdmin
|
||||
)
|
||||
|
||||
var (
|
||||
supportedIDPLoginEvents = []int{IDPLoginAny, IDPLoginUser, IDPLoginAdmin}
|
||||
)
|
||||
|
||||
// Supported filesystem actions
|
||||
const (
|
||||
FilesystemActionRename = iota + 1
|
||||
|
@ -276,6 +293,16 @@ type EventActionHTTPConfig struct {
|
|||
Parts []HTTPPart `json:"parts,omitempty"`
|
||||
}
|
||||
|
||||
// HasJSONBody returns true if the content type header indicates a JSON body
|
||||
func (c *EventActionHTTPConfig) HasJSONBody() bool {
|
||||
for _, h := range c.Headers {
|
||||
if http.CanonicalHeaderKey(h.Key) == "Content-Type" {
|
||||
return strings.Contains(strings.ToLower(h.Value), "application/json")
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *EventActionHTTPConfig) isTimeoutNotValid() bool {
|
||||
if c.HasMultipartFiles() {
|
||||
return false
|
||||
|
@ -833,6 +860,24 @@ func (c *EventActionPasswordExpiration) validate() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// EventActionIDPAccountCheck defines the check to execute after a successful IDP login
|
||||
type EventActionIDPAccountCheck struct {
|
||||
// 0 create/update, 1 create the account if it doesn't exist
|
||||
Mode int `json:"mode,omitempty"`
|
||||
TemplateUser string `json:"template_user,omitempty"`
|
||||
TemplateAdmin string `json:"template_admin,omitempty"`
|
||||
}
|
||||
|
||||
func (c *EventActionIDPAccountCheck) validate() error {
|
||||
if c.TemplateAdmin == "" && c.TemplateUser == "" {
|
||||
return util.NewValidationError("at least a template must be set")
|
||||
}
|
||||
if c.Mode < 0 || c.Mode > 1 {
|
||||
return util.NewValidationError(fmt.Sprintf("invalid account check mode: %d", c.Mode))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// BaseEventActionOptions defines the supported configuration options for a base event actions
|
||||
type BaseEventActionOptions struct {
|
||||
HTTPConfig EventActionHTTPConfig `json:"http_config"`
|
||||
|
@ -841,6 +886,7 @@ type BaseEventActionOptions struct {
|
|||
RetentionConfig EventActionDataRetentionConfig `json:"retention_config"`
|
||||
FsConfig EventActionFilesystemConfig `json:"fs_config"`
|
||||
PwdExpirationConfig EventActionPasswordExpiration `json:"pwd_expiration_config"`
|
||||
IDPConfig EventActionIDPAccountCheck `json:"idp_config"`
|
||||
}
|
||||
|
||||
func (o *BaseEventActionOptions) getACopy() BaseEventActionOptions {
|
||||
|
@ -901,6 +947,11 @@ func (o *BaseEventActionOptions) getACopy() BaseEventActionOptions {
|
|||
PwdExpirationConfig: EventActionPasswordExpiration{
|
||||
Threshold: o.PwdExpirationConfig.Threshold,
|
||||
},
|
||||
IDPConfig: EventActionIDPAccountCheck{
|
||||
Mode: o.IDPConfig.Mode,
|
||||
TemplateUser: o.IDPConfig.TemplateUser,
|
||||
TemplateAdmin: o.IDPConfig.TemplateAdmin,
|
||||
},
|
||||
FsConfig: o.FsConfig.getACopy(),
|
||||
}
|
||||
}
|
||||
|
@ -933,6 +984,7 @@ func (o *BaseEventActionOptions) validate(action int, name string) error {
|
|||
o.RetentionConfig = EventActionDataRetentionConfig{}
|
||||
o.FsConfig = EventActionFilesystemConfig{}
|
||||
o.PwdExpirationConfig = EventActionPasswordExpiration{}
|
||||
o.IDPConfig = EventActionIDPAccountCheck{}
|
||||
return o.HTTPConfig.validate(name)
|
||||
case ActionTypeCommand:
|
||||
o.HTTPConfig = EventActionHTTPConfig{}
|
||||
|
@ -940,6 +992,7 @@ func (o *BaseEventActionOptions) validate(action int, name string) error {
|
|||
o.RetentionConfig = EventActionDataRetentionConfig{}
|
||||
o.FsConfig = EventActionFilesystemConfig{}
|
||||
o.PwdExpirationConfig = EventActionPasswordExpiration{}
|
||||
o.IDPConfig = EventActionIDPAccountCheck{}
|
||||
return o.CmdConfig.validate()
|
||||
case ActionTypeEmail:
|
||||
o.HTTPConfig = EventActionHTTPConfig{}
|
||||
|
@ -947,6 +1000,7 @@ func (o *BaseEventActionOptions) validate(action int, name string) error {
|
|||
o.RetentionConfig = EventActionDataRetentionConfig{}
|
||||
o.FsConfig = EventActionFilesystemConfig{}
|
||||
o.PwdExpirationConfig = EventActionPasswordExpiration{}
|
||||
o.IDPConfig = EventActionIDPAccountCheck{}
|
||||
return o.EmailConfig.validate()
|
||||
case ActionTypeDataRetentionCheck:
|
||||
o.HTTPConfig = EventActionHTTPConfig{}
|
||||
|
@ -954,6 +1008,7 @@ func (o *BaseEventActionOptions) validate(action int, name string) error {
|
|||
o.EmailConfig = EventActionEmailConfig{}
|
||||
o.FsConfig = EventActionFilesystemConfig{}
|
||||
o.PwdExpirationConfig = EventActionPasswordExpiration{}
|
||||
o.IDPConfig = EventActionIDPAccountCheck{}
|
||||
return o.RetentionConfig.validate()
|
||||
case ActionTypeFilesystem:
|
||||
o.HTTPConfig = EventActionHTTPConfig{}
|
||||
|
@ -961,6 +1016,7 @@ func (o *BaseEventActionOptions) validate(action int, name string) error {
|
|||
o.EmailConfig = EventActionEmailConfig{}
|
||||
o.RetentionConfig = EventActionDataRetentionConfig{}
|
||||
o.PwdExpirationConfig = EventActionPasswordExpiration{}
|
||||
o.IDPConfig = EventActionIDPAccountCheck{}
|
||||
return o.FsConfig.validate()
|
||||
case ActionTypePasswordExpirationCheck:
|
||||
o.HTTPConfig = EventActionHTTPConfig{}
|
||||
|
@ -968,7 +1024,16 @@ func (o *BaseEventActionOptions) validate(action int, name string) error {
|
|||
o.EmailConfig = EventActionEmailConfig{}
|
||||
o.RetentionConfig = EventActionDataRetentionConfig{}
|
||||
o.FsConfig = EventActionFilesystemConfig{}
|
||||
o.IDPConfig = EventActionIDPAccountCheck{}
|
||||
return o.PwdExpirationConfig.validate()
|
||||
case ActionTypeIDPAccountCheck:
|
||||
o.HTTPConfig = EventActionHTTPConfig{}
|
||||
o.CmdConfig = EventActionCommandConfig{}
|
||||
o.EmailConfig = EventActionEmailConfig{}
|
||||
o.RetentionConfig = EventActionDataRetentionConfig{}
|
||||
o.FsConfig = EventActionFilesystemConfig{}
|
||||
o.PwdExpirationConfig = EventActionPasswordExpiration{}
|
||||
return o.IDPConfig.validate()
|
||||
default:
|
||||
o.HTTPConfig = EventActionHTTPConfig{}
|
||||
o.CmdConfig = EventActionCommandConfig{}
|
||||
|
@ -976,6 +1041,7 @@ func (o *BaseEventActionOptions) validate(action int, name string) error {
|
|||
o.RetentionConfig = EventActionDataRetentionConfig{}
|
||||
o.FsConfig = EventActionFilesystemConfig{}
|
||||
o.PwdExpirationConfig = EventActionPasswordExpiration{}
|
||||
o.IDPConfig = EventActionIDPAccountCheck{}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -1086,12 +1152,14 @@ func (a *EventAction) validateAssociation(trigger int, fsEvents []string) error
|
|||
}
|
||||
}
|
||||
if a.Options.ExecuteSync {
|
||||
if trigger != EventTriggerFsEvent {
|
||||
return util.NewValidationError("sync execution is only supported for some filesystem events")
|
||||
if trigger != EventTriggerFsEvent && trigger != EventTriggerIDPLogin {
|
||||
return util.NewValidationError("sync execution is only supported for some filesystem events and Identity Provider logins")
|
||||
}
|
||||
for _, ev := range fsEvents {
|
||||
if !util.Contains(allowedSyncFsEvents, ev) {
|
||||
return util.NewValidationError("sync execution is only supported for upload and pre-* events")
|
||||
if trigger == EventTriggerFsEvent {
|
||||
for _, ev := range fsEvents {
|
||||
if !util.Contains(allowedSyncFsEvents, ev) {
|
||||
return util.NewValidationError("sync execution is only supported for upload and pre-* events")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1213,10 +1281,12 @@ func (s *Schedule) validate() error {
|
|||
// EventConditions defines the conditions for an event rule
|
||||
type EventConditions struct {
|
||||
// Only one between FsEvents, ProviderEvents and Schedule is allowed
|
||||
FsEvents []string `json:"fs_events,omitempty"`
|
||||
ProviderEvents []string `json:"provider_events,omitempty"`
|
||||
Schedules []Schedule `json:"schedules,omitempty"`
|
||||
Options ConditionOptions `json:"options"`
|
||||
FsEvents []string `json:"fs_events,omitempty"`
|
||||
ProviderEvents []string `json:"provider_events,omitempty"`
|
||||
Schedules []Schedule `json:"schedules,omitempty"`
|
||||
// 0 any, 1 user, 2 admin
|
||||
IDPLoginEvent int `json:"idp_login_event,omitempty"`
|
||||
Options ConditionOptions `json:"options"`
|
||||
}
|
||||
|
||||
func (c *EventConditions) getACopy() EventConditions {
|
||||
|
@ -1238,16 +1308,30 @@ func (c *EventConditions) getACopy() EventConditions {
|
|||
FsEvents: fsEvents,
|
||||
ProviderEvents: providerEvents,
|
||||
Schedules: schedules,
|
||||
IDPLoginEvent: c.IDPLoginEvent,
|
||||
Options: c.Options.getACopy(),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *EventConditions) validateSchedules() error {
|
||||
if len(c.Schedules) == 0 {
|
||||
return util.NewValidationError("at least one schedule is required")
|
||||
}
|
||||
for _, schedule := range c.Schedules {
|
||||
if err := schedule.validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *EventConditions) validate(trigger int) error {
|
||||
switch trigger {
|
||||
case EventTriggerFsEvent:
|
||||
c.ProviderEvents = nil
|
||||
c.Schedules = nil
|
||||
c.Options.ProviderObjects = nil
|
||||
c.IDPLoginEvent = 0
|
||||
if len(c.FsEvents) == 0 {
|
||||
return util.NewValidationError("at least one filesystem event is required")
|
||||
}
|
||||
|
@ -1264,6 +1348,7 @@ func (c *EventConditions) validate(trigger int) error {
|
|||
c.Options.Protocols = nil
|
||||
c.Options.MinFileSize = 0
|
||||
c.Options.MaxFileSize = 0
|
||||
c.IDPLoginEvent = 0
|
||||
if len(c.ProviderEvents) == 0 {
|
||||
return util.NewValidationError("at least one provider event is required")
|
||||
}
|
||||
|
@ -1280,13 +1365,9 @@ func (c *EventConditions) validate(trigger int) error {
|
|||
c.Options.MinFileSize = 0
|
||||
c.Options.MaxFileSize = 0
|
||||
c.Options.ProviderObjects = nil
|
||||
if len(c.Schedules) == 0 {
|
||||
return util.NewValidationError("at least one schedule is required")
|
||||
}
|
||||
for _, schedule := range c.Schedules {
|
||||
if err := schedule.validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
c.IDPLoginEvent = 0
|
||||
if err := c.validateSchedules(); err != nil {
|
||||
return err
|
||||
}
|
||||
case EventTriggerIPBlocked, EventTriggerCertificate:
|
||||
c.FsEvents = nil
|
||||
|
@ -1299,6 +1380,7 @@ func (c *EventConditions) validate(trigger int) error {
|
|||
c.Options.MinFileSize = 0
|
||||
c.Options.MaxFileSize = 0
|
||||
c.Schedules = nil
|
||||
c.IDPLoginEvent = 0
|
||||
case EventTriggerOnDemand:
|
||||
c.FsEvents = nil
|
||||
c.ProviderEvents = nil
|
||||
|
@ -1308,7 +1390,21 @@ func (c *EventConditions) validate(trigger int) error {
|
|||
c.Options.MaxFileSize = 0
|
||||
c.Options.ProviderObjects = nil
|
||||
c.Schedules = nil
|
||||
c.IDPLoginEvent = 0
|
||||
c.Options.ConcurrentExecution = false
|
||||
case EventTriggerIDPLogin:
|
||||
c.FsEvents = nil
|
||||
c.ProviderEvents = nil
|
||||
c.Options.GroupNames = nil
|
||||
c.Options.RoleNames = nil
|
||||
c.Options.FsPaths = nil
|
||||
c.Options.Protocols = nil
|
||||
c.Options.MinFileSize = 0
|
||||
c.Options.MaxFileSize = 0
|
||||
c.Schedules = nil
|
||||
if !util.Contains(supportedIDPLoginEvents, c.IDPLoginEvent) {
|
||||
return util.NewValidationError(fmt.Sprintf("invalid Identity Provider login event %d", c.IDPLoginEvent))
|
||||
}
|
||||
default:
|
||||
c.FsEvents = nil
|
||||
c.ProviderEvents = nil
|
||||
|
@ -1319,6 +1415,7 @@ func (c *EventConditions) validate(trigger int) error {
|
|||
c.Options.MinFileSize = 0
|
||||
c.Options.MaxFileSize = 0
|
||||
c.Schedules = nil
|
||||
c.IDPLoginEvent = 0
|
||||
}
|
||||
|
||||
return c.Options.validate()
|
||||
|
@ -1453,7 +1550,7 @@ func (r *EventRule) validateMandatorySyncActions() error {
|
|||
}
|
||||
for _, ev := range r.Conditions.FsEvents {
|
||||
if util.Contains(mandatorySyncFsEvents, ev) {
|
||||
return util.NewValidationError(fmt.Sprintf("event %s requires at least a sync action", ev))
|
||||
return util.NewValidationError(fmt.Sprintf("event %q requires at least a sync action", ev))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
@ -1508,6 +1605,39 @@ func (r *EventRule) hasUserAssociated(providerObjectType string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (r *EventRule) checkActions(providerObjectType string) error {
|
||||
numSyncAction := 0
|
||||
hasIDPAccountCheck := false
|
||||
for _, action := range r.Actions {
|
||||
if action.Options.ExecuteSync {
|
||||
numSyncAction++
|
||||
}
|
||||
if action.Type == ActionTypeEmail && action.BaseEventAction.Options.EmailConfig.hasFilesAttachments() {
|
||||
if !r.hasUserAssociated(providerObjectType) {
|
||||
return errors.New("cannot send an email with attachments for a rule with no user associated")
|
||||
}
|
||||
}
|
||||
if action.Type == ActionTypeHTTP && action.BaseEventAction.Options.HTTPConfig.HasMultipartFiles() {
|
||||
if !r.hasUserAssociated(providerObjectType) {
|
||||
return errors.New("cannot upload file/s for a rule with no user associated")
|
||||
}
|
||||
}
|
||||
if action.Type == ActionTypeIDPAccountCheck {
|
||||
if r.Trigger != EventTriggerIDPLogin {
|
||||
return errors.New("IDP account check action is only supported for IDP login trigger")
|
||||
}
|
||||
if !action.Options.ExecuteSync {
|
||||
return errors.New("IDP account check must be a sync action")
|
||||
}
|
||||
hasIDPAccountCheck = true
|
||||
}
|
||||
}
|
||||
if hasIDPAccountCheck && numSyncAction != 1 {
|
||||
return errors.New("IDP account check must be the only sync action")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckActionsConsistency returns an error if the actions cannot be executed
|
||||
func (r *EventRule) CheckActionsConsistency(providerObjectType string) error {
|
||||
switch r.Trigger {
|
||||
|
@ -1528,19 +1658,7 @@ func (r *EventRule) CheckActionsConsistency(providerObjectType string) error {
|
|||
return err
|
||||
}
|
||||
}
|
||||
for _, action := range r.Actions {
|
||||
if action.Type == ActionTypeEmail && action.BaseEventAction.Options.EmailConfig.hasFilesAttachments() {
|
||||
if !r.hasUserAssociated(providerObjectType) {
|
||||
return errors.New("cannot send an email with attachments for a rule with no user associated")
|
||||
}
|
||||
}
|
||||
if action.Type == ActionTypeHTTP && action.BaseEventAction.Options.HTTPConfig.HasMultipartFiles() {
|
||||
if !r.hasUserAssociated(providerObjectType) {
|
||||
return errors.New("cannot upload file/s for a rule with no user associated")
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return r.checkActions(providerObjectType)
|
||||
}
|
||||
|
||||
// PrepareForRendering prepares an EventRule for rendering.
|
||||
|
|
|
@ -431,7 +431,7 @@ func (p *MemoryProvider) updateUser(user *User) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (p *MemoryProvider) deleteUser(user User, softDelete bool) error {
|
||||
func (p *MemoryProvider) deleteUser(user User, _ bool) error {
|
||||
p.dbHandle.Lock()
|
||||
defer p.dbHandle.Unlock()
|
||||
if p.dbHandle.isClosed {
|
||||
|
@ -898,7 +898,7 @@ func (p *MemoryProvider) updateFolderQuota(name string, filesAdd int, sizeAdd in
|
|||
return nil
|
||||
}
|
||||
|
||||
func (p *MemoryProvider) getGroups(limit, offset int, order string, minimal bool) ([]Group, error) {
|
||||
func (p *MemoryProvider) getGroups(limit, offset int, order string, _ bool) ([]Group, error) {
|
||||
p.dbHandle.Lock()
|
||||
defer p.dbHandle.Unlock()
|
||||
if p.dbHandle.isClosed {
|
||||
|
@ -1422,7 +1422,7 @@ func (p *MemoryProvider) folderExistsInternal(name string) (vfs.BaseVirtualFolde
|
|||
return vfs.BaseVirtualFolder{}, util.NewRecordNotFoundError(fmt.Sprintf("folder %q does not exist", name))
|
||||
}
|
||||
|
||||
func (p *MemoryProvider) getFolders(limit, offset int, order string, minimal bool) ([]vfs.BaseVirtualFolder, error) {
|
||||
func (p *MemoryProvider) getFolders(limit, offset int, order string, _ bool) ([]vfs.BaseVirtualFolder, error) {
|
||||
folders := make([]vfs.BaseVirtualFolder, 0, limit)
|
||||
var err error
|
||||
p.dbHandle.Lock()
|
||||
|
@ -2006,75 +2006,75 @@ func (p *MemoryProvider) updateShareLastUse(shareID string, numTokens int) error
|
|||
return nil
|
||||
}
|
||||
|
||||
func (p *MemoryProvider) getDefenderHosts(from int64, limit int) ([]DefenderEntry, error) {
|
||||
func (p *MemoryProvider) getDefenderHosts(_ int64, _ int) ([]DefenderEntry, error) {
|
||||
return nil, ErrNotImplemented
|
||||
}
|
||||
|
||||
func (p *MemoryProvider) getDefenderHostByIP(ip string, from int64) (DefenderEntry, error) {
|
||||
func (p *MemoryProvider) getDefenderHostByIP(_ string, _ int64) (DefenderEntry, error) {
|
||||
return DefenderEntry{}, ErrNotImplemented
|
||||
}
|
||||
|
||||
func (p *MemoryProvider) isDefenderHostBanned(ip string) (DefenderEntry, error) {
|
||||
func (p *MemoryProvider) isDefenderHostBanned(_ string) (DefenderEntry, error) {
|
||||
return DefenderEntry{}, ErrNotImplemented
|
||||
}
|
||||
|
||||
func (p *MemoryProvider) updateDefenderBanTime(ip string, minutes int) error {
|
||||
func (p *MemoryProvider) updateDefenderBanTime(_ string, _ int) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func (p *MemoryProvider) deleteDefenderHost(ip string) error {
|
||||
func (p *MemoryProvider) deleteDefenderHost(_ string) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func (p *MemoryProvider) addDefenderEvent(ip string, score int) error {
|
||||
func (p *MemoryProvider) addDefenderEvent(_ string, _ int) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func (p *MemoryProvider) setDefenderBanTime(ip string, banTime int64) error {
|
||||
func (p *MemoryProvider) setDefenderBanTime(_ string, _ int64) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func (p *MemoryProvider) cleanupDefender(from int64) error {
|
||||
func (p *MemoryProvider) cleanupDefender(_ int64) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func (p *MemoryProvider) addActiveTransfer(transfer ActiveTransfer) error {
|
||||
func (p *MemoryProvider) addActiveTransfer(_ ActiveTransfer) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func (p *MemoryProvider) updateActiveTransferSizes(ulSize, dlSize, transferID int64, connectionID string) error {
|
||||
func (p *MemoryProvider) updateActiveTransferSizes(_, _, _ int64, _ string) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func (p *MemoryProvider) removeActiveTransfer(transferID int64, connectionID string) error {
|
||||
func (p *MemoryProvider) removeActiveTransfer(_ int64, _ string) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func (p *MemoryProvider) cleanupActiveTransfers(before time.Time) error {
|
||||
func (p *MemoryProvider) cleanupActiveTransfers(_ time.Time) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func (p *MemoryProvider) getActiveTransfers(from time.Time) ([]ActiveTransfer, error) {
|
||||
func (p *MemoryProvider) getActiveTransfers(_ time.Time) ([]ActiveTransfer, error) {
|
||||
return nil, ErrNotImplemented
|
||||
}
|
||||
|
||||
func (p *MemoryProvider) addSharedSession(session Session) error {
|
||||
func (p *MemoryProvider) addSharedSession(_ Session) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func (p *MemoryProvider) deleteSharedSession(key string) error {
|
||||
func (p *MemoryProvider) deleteSharedSession(_ string) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func (p *MemoryProvider) getSharedSession(key string) (Session, error) {
|
||||
func (p *MemoryProvider) getSharedSession(_ string) (Session, error) {
|
||||
return Session{}, ErrNotImplemented
|
||||
}
|
||||
|
||||
func (p *MemoryProvider) cleanupSharedSessions(sessionType SessionType, before int64) error {
|
||||
func (p *MemoryProvider) cleanupSharedSessions(_ SessionType, _ int64) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func (p *MemoryProvider) getEventActions(limit, offset int, order string, minimal bool) ([]BaseEventAction, error) {
|
||||
func (p *MemoryProvider) getEventActions(limit, offset int, order string, _ bool) ([]BaseEventAction, error) {
|
||||
p.dbHandle.Lock()
|
||||
defer p.dbHandle.Unlock()
|
||||
if p.dbHandle.isClosed {
|
||||
|
@ -2395,7 +2395,7 @@ func (p *MemoryProvider) updateEventRule(rule *EventRule) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (p *MemoryProvider) deleteEventRule(rule EventRule, softDelete bool) error {
|
||||
func (p *MemoryProvider) deleteEventRule(rule EventRule, _ bool) error {
|
||||
p.dbHandle.Lock()
|
||||
defer p.dbHandle.Unlock()
|
||||
if p.dbHandle.isClosed {
|
||||
|
@ -2420,19 +2420,19 @@ func (p *MemoryProvider) deleteEventRule(rule EventRule, softDelete bool) error
|
|||
return nil
|
||||
}
|
||||
|
||||
func (*MemoryProvider) getTaskByName(name string) (Task, error) {
|
||||
func (*MemoryProvider) getTaskByName(_ string) (Task, error) {
|
||||
return Task{}, ErrNotImplemented
|
||||
}
|
||||
|
||||
func (*MemoryProvider) addTask(name string) error {
|
||||
func (*MemoryProvider) addTask(_ string) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func (*MemoryProvider) updateTask(name string, version int64) error {
|
||||
func (*MemoryProvider) updateTask(_ string, _ int64) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func (*MemoryProvider) updateTaskTimestamp(name string) error {
|
||||
func (*MemoryProvider) updateTaskTimestamp(_ string) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
|
@ -2440,7 +2440,7 @@ func (*MemoryProvider) addNode() error {
|
|||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func (*MemoryProvider) getNodeByName(name string) (Node, error) {
|
||||
func (*MemoryProvider) getNodeByName(_ string) (Node, error) {
|
||||
return Node{}, ErrNotImplemented
|
||||
}
|
||||
|
||||
|
@ -2550,7 +2550,7 @@ func (p *MemoryProvider) deleteRole(role Role) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (p *MemoryProvider) getRoles(limit int, offset int, order string, minimal bool) ([]Role, error) {
|
||||
func (p *MemoryProvider) getRoles(limit int, offset int, order string, _ bool) ([]Role, error) {
|
||||
p.dbHandle.Lock()
|
||||
defer p.dbHandle.Unlock()
|
||||
|
||||
|
@ -2662,7 +2662,7 @@ func (p *MemoryProvider) updateIPListEntry(entry *IPListEntry) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (p *MemoryProvider) deleteIPListEntry(entry IPListEntry, softDelete bool) error {
|
||||
func (p *MemoryProvider) deleteIPListEntry(entry IPListEntry, _ bool) error {
|
||||
if err := entry.validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -2721,7 +2721,7 @@ func (p *MemoryProvider) getIPListEntries(listType IPListType, filter, from, ord
|
|||
return entries, nil
|
||||
}
|
||||
|
||||
func (p *MemoryProvider) getRecentlyUpdatedIPListEntries(after int64) ([]IPListEntry, error) {
|
||||
func (p *MemoryProvider) getRecentlyUpdatedIPListEntries(_ int64) ([]IPListEntry, error) {
|
||||
return nil, ErrNotImplemented
|
||||
}
|
||||
|
||||
|
@ -3293,7 +3293,7 @@ func (p *MemoryProvider) migrateDatabase() error {
|
|||
return ErrNoInitRequired
|
||||
}
|
||||
|
||||
func (p *MemoryProvider) revertDatabase(targetVersion int) error {
|
||||
func (p *MemoryProvider) revertDatabase(_ int) error {
|
||||
return errors.New("memory provider does not store data, revert not possible")
|
||||
}
|
||||
|
||||
|
|
|
@ -3491,7 +3491,7 @@ func sqlCommonUpdateEventAction(action *BaseEventAction, dbHandle *sql.DB) error
|
|||
return err
|
||||
}
|
||||
q = getUpdateRulesTimestampQuery()
|
||||
_, err = tx.ExecContext(ctx, q, util.GetTimeAsMsSinceEpoch(time.Now()), action.ID)
|
||||
_, err = tx.ExecContext(ctx, q, util.GetTimeAsMsSinceEpoch(time.Now()), action.Name)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
|
|
@ -603,7 +603,7 @@ func (*SQLiteProvider) addNode() error {
|
|||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func (*SQLiteProvider) getNodeByName(name string) (Node, error) {
|
||||
func (*SQLiteProvider) getNodeByName(_ string) (Node, error) {
|
||||
return Node{}, ErrNotImplemented
|
||||
}
|
||||
|
||||
|
|
|
@ -1106,8 +1106,8 @@ func getClearRuleActionMappingQuery() string {
|
|||
}
|
||||
|
||||
func getUpdateRulesTimestampQuery() string {
|
||||
return fmt.Sprintf(`UPDATE %s SET updated_at=%s WHERE id IN (SELECT rule_id FROM %s WHERE action_id = %s)`,
|
||||
sqlTableEventsRules, sqlPlaceholders[0], sqlTableRulesActionsMapping, sqlPlaceholders[1])
|
||||
return fmt.Sprintf(`UPDATE %s SET updated_at=%s WHERE id IN (SELECT rule_id FROM %s WHERE action_id = (SELECT id from %s WHERE name = %s))`,
|
||||
sqlTableEventsRules, sqlPlaceholders[0], sqlTableRulesActionsMapping, sqlTableEventsActions, sqlPlaceholders[1])
|
||||
}
|
||||
|
||||
func getRelatedActionsForRulesQuery(rules []EventRule) string {
|
||||
|
|
|
@ -90,29 +90,29 @@ func (c *Connection) GetCommand() string {
|
|||
}
|
||||
|
||||
// Create is not implemented we use ClientDriverExtentionFileTransfer
|
||||
func (c *Connection) Create(name string) (afero.File, error) {
|
||||
func (c *Connection) Create(_ string) (afero.File, error) {
|
||||
return nil, errNotImplemented
|
||||
}
|
||||
|
||||
// Mkdir creates a directory using the connection filesystem
|
||||
func (c *Connection) Mkdir(name string, perm os.FileMode) error {
|
||||
func (c *Connection) Mkdir(name string, _ os.FileMode) error {
|
||||
c.UpdateLastActivity()
|
||||
|
||||
return c.CreateDir(name, true)
|
||||
}
|
||||
|
||||
// MkdirAll is not implemented, we don't need it
|
||||
func (c *Connection) MkdirAll(path string, perm os.FileMode) error {
|
||||
func (c *Connection) MkdirAll(_ string, _ os.FileMode) error {
|
||||
return errNotImplemented
|
||||
}
|
||||
|
||||
// Open is not implemented we use ClientDriverExtentionFileTransfer and ClientDriverExtensionFileList
|
||||
func (c *Connection) Open(name string) (afero.File, error) {
|
||||
func (c *Connection) Open(_ string) (afero.File, error) {
|
||||
return nil, errNotImplemented
|
||||
}
|
||||
|
||||
// OpenFile is not implemented we use ClientDriverExtentionFileTransfer
|
||||
func (c *Connection) OpenFile(name string, flag int, perm os.FileMode) (afero.File, error) {
|
||||
func (c *Connection) OpenFile(_ string, _ int, _ os.FileMode) (afero.File, error) {
|
||||
return nil, errNotImplemented
|
||||
}
|
||||
|
||||
|
@ -140,7 +140,7 @@ func (c *Connection) Remove(name string) error {
|
|||
}
|
||||
|
||||
// RemoveAll is not implemented, we don't need it
|
||||
func (c *Connection) RemoveAll(path string) error {
|
||||
func (c *Connection) RemoveAll(_ string) error {
|
||||
return errNotImplemented
|
||||
}
|
||||
|
||||
|
@ -178,7 +178,7 @@ func (c *Connection) Name() string {
|
|||
}
|
||||
|
||||
// Chown changes the uid and gid of the named file
|
||||
func (c *Connection) Chown(name string, uid, gid int) error {
|
||||
func (c *Connection) Chown(_ string, _, _ int) error {
|
||||
c.UpdateLastActivity()
|
||||
|
||||
return common.ErrOpUnsupported
|
||||
|
@ -270,7 +270,7 @@ func (c *Connection) GetAvailableSpace(dirName string) (int64, error) {
|
|||
}
|
||||
|
||||
// AllocateSpace implements ClientDriverExtensionAllocate interface
|
||||
func (c *Connection) AllocateSpace(size int) error {
|
||||
func (c *Connection) AllocateSpace(_ int) error {
|
||||
c.UpdateLastActivity()
|
||||
// we treat ALLO as NOOP see RFC 959
|
||||
return nil
|
||||
|
|
|
@ -282,11 +282,11 @@ func (cc mockFTPClientContext) Path() string {
|
|||
return ""
|
||||
}
|
||||
|
||||
func (cc mockFTPClientContext) SetPath(name string) {}
|
||||
func (cc mockFTPClientContext) SetPath(_ string) {}
|
||||
|
||||
func (cc mockFTPClientContext) SetListPath(name string) {}
|
||||
func (cc mockFTPClientContext) SetListPath(_ string) {}
|
||||
|
||||
func (cc mockFTPClientContext) SetDebug(debug bool) {}
|
||||
func (cc mockFTPClientContext) SetDebug(_ bool) {}
|
||||
|
||||
func (cc mockFTPClientContext) Debug() bool {
|
||||
return false
|
||||
|
@ -328,7 +328,7 @@ func (cc mockFTPClientContext) HasTLSForTransfers() bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (cc mockFTPClientContext) SetTLSRequirement(requirement ftpserver.TLSRequirement) error {
|
||||
func (cc mockFTPClientContext) SetTLSRequirement(_ ftpserver.TLSRequirement) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -380,7 +380,7 @@ func (fs MockOsFs) Lstat(name string) (os.FileInfo, error) {
|
|||
}
|
||||
|
||||
// Remove removes the named file or (empty) directory.
|
||||
func (fs MockOsFs) Remove(name string, isDir bool) error {
|
||||
func (fs MockOsFs) Remove(name string, _ bool) error {
|
||||
if fs.err != nil {
|
||||
return fs.err
|
||||
}
|
||||
|
|
|
@ -183,7 +183,7 @@ func restoreBackup(content []byte, inputFile string, scanQuota, mode int, execut
|
|||
return util.NewValidationError(fmt.Sprintf("unable to parse backup content: %v", err))
|
||||
}
|
||||
|
||||
if err = RestoreConfigs(dump.Configs, inputFile, mode, executor, ipAddress, role); err != nil {
|
||||
if err = RestoreConfigs(dump.Configs, mode, executor, ipAddress, role); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -423,7 +423,7 @@ func RestoreAdmins(admins []dataprovider.Admin, inputFile string, mode int, exec
|
|||
}
|
||||
|
||||
// RestoreConfigs restores the specified provider configs
|
||||
func RestoreConfigs(configs *dataprovider.Configs, inputFile string, mode int, executor, ipAddress,
|
||||
func RestoreConfigs(configs *dataprovider.Configs, mode int, executor, ipAddress,
|
||||
executorRole string,
|
||||
) error {
|
||||
if configs == nil {
|
||||
|
|
|
@ -298,6 +298,7 @@ func (c *jwtTokenClaims) removeCookie(w http.ResponseWriter, r *http.Request, co
|
|||
Secure: isTLS(r),
|
||||
SameSite: http.SameSiteStrictMode,
|
||||
})
|
||||
w.Header().Add("Cache-Control", `no-cache="Set-Cookie"`)
|
||||
invalidateToken(r)
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ func setFlashMessage(w http.ResponseWriter, r *http.Request, value string) {
|
|||
Secure: isTLS(r),
|
||||
SameSite: http.SameSiteLaxMode,
|
||||
})
|
||||
w.Header().Add("Cache-Control", `no-cache="Set-Cookie"`)
|
||||
}
|
||||
|
||||
func getFlashMessage(w http.ResponseWriter, r *http.Request) string {
|
||||
|
|
|
@ -315,15 +315,15 @@ func (t *throttledReader) HasSizeLimit() bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (t *throttledReader) Truncate(fsPath string, size int64) (int64, error) {
|
||||
func (t *throttledReader) Truncate(_ string, _ int64) (int64, error) {
|
||||
return 0, vfs.ErrVfsUnsupported
|
||||
}
|
||||
|
||||
func (t *throttledReader) GetRealFsPath(fsPath string) string {
|
||||
func (t *throttledReader) GetRealFsPath(_ string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (t *throttledReader) SetTimes(fsPath string, atime time.Time, mtime time.Time) bool {
|
||||
func (t *throttledReader) SetTimes(_ string, _ time.Time, _ time.Time) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
|
@ -1973,6 +1973,58 @@ func TestOnDemandEventRules(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestIDPLoginEventRule(t *testing.T) {
|
||||
ruleName := "test IDP login rule"
|
||||
a := dataprovider.BaseEventAction{
|
||||
Name: "a",
|
||||
Type: dataprovider.ActionTypeIDPAccountCheck,
|
||||
Options: dataprovider.BaseEventActionOptions{
|
||||
IDPConfig: dataprovider.EventActionIDPAccountCheck{
|
||||
Mode: 1,
|
||||
TemplateUser: `{"username": "user"}`,
|
||||
TemplateAdmin: `{"username": "admin"}`,
|
||||
},
|
||||
},
|
||||
}
|
||||
action, resp, err := httpdtest.AddEventAction(a, http.StatusCreated)
|
||||
assert.NoError(t, err, string(resp))
|
||||
r := dataprovider.EventRule{
|
||||
Name: ruleName,
|
||||
Status: 1,
|
||||
Trigger: dataprovider.EventTriggerIDPLogin,
|
||||
Conditions: dataprovider.EventConditions{
|
||||
IDPLoginEvent: 1,
|
||||
Options: dataprovider.ConditionOptions{
|
||||
Names: []dataprovider.ConditionPattern{
|
||||
{
|
||||
Pattern: "username",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Actions: []dataprovider.EventAction{
|
||||
{
|
||||
BaseEventAction: dataprovider.BaseEventAction{
|
||||
Name: a.Name,
|
||||
},
|
||||
Options: dataprovider.EventActionOptions{
|
||||
ExecuteSync: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
rule, _, err := httpdtest.AddEventRule(r, http.StatusCreated)
|
||||
assert.NoError(t, err)
|
||||
rule.Status = 0
|
||||
_, _, err = httpdtest.UpdateEventRule(rule, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, err = httpdtest.RemoveEventRule(rule, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
_, err = httpdtest.RemoveEventAction(action, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestEventActionValidation(t *testing.T) {
|
||||
action := dataprovider.BaseEventAction{
|
||||
Name: "",
|
||||
|
@ -2235,6 +2287,15 @@ func TestEventActionValidation(t *testing.T) {
|
|||
_, resp, err = httpdtest.AddEventAction(action, http.StatusBadRequest)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, string(resp), "threshold must be greater than 0")
|
||||
action.Type = dataprovider.ActionTypeIDPAccountCheck
|
||||
_, resp, err = httpdtest.AddEventAction(action, http.StatusBadRequest)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, string(resp), "at least a template must be set")
|
||||
action.Options.IDPConfig.TemplateAdmin = "{}"
|
||||
action.Options.IDPConfig.Mode = 100
|
||||
_, resp, err = httpdtest.AddEventAction(action, http.StatusBadRequest)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, string(resp), "invalid account check mode")
|
||||
}
|
||||
|
||||
func TestEventRuleValidation(t *testing.T) {
|
||||
|
@ -2367,7 +2428,7 @@ func TestEventRuleValidation(t *testing.T) {
|
|||
}
|
||||
_, resp, err = httpdtest.AddEventRule(rule, http.StatusBadRequest)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, string(resp), "event pre-upload requires at least a sync action")
|
||||
assert.Contains(t, string(resp), "requires at least a sync action")
|
||||
rule.Actions = []dataprovider.EventAction{
|
||||
{
|
||||
BaseEventAction: dataprovider.BaseEventAction{
|
||||
|
@ -2431,6 +2492,11 @@ func TestEventRuleValidation(t *testing.T) {
|
|||
}
|
||||
_, resp, err = httpdtest.AddEventRule(rule, http.StatusInternalServerError)
|
||||
assert.NoError(t, err, string(resp))
|
||||
rule.Trigger = dataprovider.EventTriggerIDPLogin
|
||||
rule.Conditions.IDPLoginEvent = 100
|
||||
_, resp, err = httpdtest.AddEventRule(rule, http.StatusBadRequest)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, string(resp), "invalid Identity Provider login event")
|
||||
}
|
||||
|
||||
func TestUserTransferLimits(t *testing.T) {
|
||||
|
@ -21532,6 +21598,26 @@ func TestWebEventAction(t *testing.T) {
|
|||
assert.Equal(t, 0, actionGet.Options.CmdConfig.Timeout)
|
||||
assert.Len(t, actionGet.Options.CmdConfig.EnvVars, 0)
|
||||
|
||||
action.Type = dataprovider.ActionTypeIDPAccountCheck
|
||||
form.Set("type", fmt.Sprintf("%d", action.Type))
|
||||
form.Set("idp_mode", "1")
|
||||
form.Set("idp_user", `{"username":"user"}`)
|
||||
form.Set("idp_admin", `{"username":"admin"}`)
|
||||
form.Set("pwd_expiration_threshold", strconv.Itoa(action.Options.PwdExpirationConfig.Threshold))
|
||||
req, err = http.NewRequest(http.MethodPost, path.Join(webAdminEventActionPath, action.Name),
|
||||
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.StatusSeeOther, rr)
|
||||
actionGet, _, err = httpdtest.GetEventActionByName(action.Name, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, action.Type, actionGet.Type)
|
||||
assert.Equal(t, 1, actionGet.Options.IDPConfig.Mode)
|
||||
assert.Contains(t, actionGet.Options.IDPConfig.TemplateUser, `"user"`)
|
||||
assert.Contains(t, actionGet.Options.IDPConfig.TemplateAdmin, `"admin"`)
|
||||
|
||||
req, err = http.NewRequest(http.MethodDelete, path.Join(webAdminEventActionPath, action.Name), nil)
|
||||
assert.NoError(t, err)
|
||||
setBearerForReq(req, apiToken)
|
||||
|
@ -21785,6 +21871,36 @@ func TestWebEventRule(t *testing.T) {
|
|||
assert.Equal(t, rule.Actions[0].Name, ruleGet.Actions[0].Name)
|
||||
assert.Equal(t, rule.Actions[0].Order, ruleGet.Actions[0].Order)
|
||||
}
|
||||
rule.Trigger = dataprovider.EventTriggerIDPLogin
|
||||
form.Set("trigger", fmt.Sprintf("%d", rule.Trigger))
|
||||
form.Set("idp_login_event", "1")
|
||||
req, err = http.NewRequest(http.MethodPost, path.Join(webAdminEventRulePath, rule.Name),
|
||||
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.StatusSeeOther, rr)
|
||||
// check the rule
|
||||
ruleGet, _, err = httpdtest.GetEventRuleByName(rule.Name, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, rule.Trigger, ruleGet.Trigger)
|
||||
assert.Equal(t, 1, ruleGet.Conditions.IDPLoginEvent)
|
||||
|
||||
form.Set("idp_login_event", "2")
|
||||
req, err = http.NewRequest(http.MethodPost, path.Join(webAdminEventRulePath, rule.Name),
|
||||
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.StatusSeeOther, rr)
|
||||
// check the rule
|
||||
ruleGet, _, err = httpdtest.GetEventRuleByName(rule.Name, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, rule.Trigger, ruleGet.Trigger)
|
||||
assert.Equal(t, 2, ruleGet.Conditions.IDPLoginEvent)
|
||||
|
||||
// update a missing rule
|
||||
req, err = http.NewRequest(http.MethodPost, path.Join(webAdminEventRulePath, rule.Name+"1"),
|
||||
bytes.NewBuffer([]byte(form.Encode())))
|
||||
|
|
|
@ -292,11 +292,11 @@ var (
|
|||
type failingWriter struct {
|
||||
}
|
||||
|
||||
func (r *failingWriter) Write(p []byte) (n int, err error) {
|
||||
func (r *failingWriter) Write(_ []byte) (n int, err error) {
|
||||
return 0, errors.New("write error")
|
||||
}
|
||||
|
||||
func (r *failingWriter) WriteHeader(statusCode int) {}
|
||||
func (r *failingWriter) WriteHeader(_ int) {}
|
||||
|
||||
func (r *failingWriter) Header() http.Header {
|
||||
return make(http.Header)
|
||||
|
|
|
@ -410,47 +410,70 @@ func (t *oidcToken) refreshUser(r *http.Request) error {
|
|||
}
|
||||
|
||||
func (t *oidcToken) getUser(r *http.Request) error {
|
||||
ipAddr := util.GetIPFromRemoteAddress(r.RemoteAddr)
|
||||
params := common.EventParams{
|
||||
Name: t.Username,
|
||||
IP: ipAddr,
|
||||
Protocol: common.ProtocolOIDC,
|
||||
Timestamp: time.Now().UnixNano(),
|
||||
Status: 1,
|
||||
}
|
||||
if t.isAdmin() {
|
||||
admin, err := dataprovider.AdminExists(t.Username)
|
||||
params.Event = common.IDPLoginAdmin
|
||||
_, admin, err := common.HandleIDPLoginEvent(params, t.CustomFields)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := admin.CanLogin(util.GetIPFromRemoteAddress(r.RemoteAddr)); err != nil {
|
||||
if admin == nil {
|
||||
a, err := dataprovider.AdminExists(t.Username)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
admin = &a
|
||||
}
|
||||
if err := admin.CanLogin(ipAddr); err != nil {
|
||||
return err
|
||||
}
|
||||
t.Permissions = admin.Permissions
|
||||
t.TokenRole = admin.Role
|
||||
t.HideUserPageSections = admin.Filters.Preferences.HideUserPageSections
|
||||
dataprovider.UpdateAdminLastLogin(&admin)
|
||||
dataprovider.UpdateAdminLastLogin(admin)
|
||||
return nil
|
||||
}
|
||||
ipAddr := util.GetIPFromRemoteAddress(r.RemoteAddr)
|
||||
user, err := dataprovider.GetUserAfterIDPAuth(t.Username, ipAddr, common.ProtocolOIDC, t.CustomFields)
|
||||
params.Event = common.IDPLoginUser
|
||||
user, _, err := common.HandleIDPLoginEvent(params, t.CustomFields)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if user == nil {
|
||||
u, err := dataprovider.GetUserAfterIDPAuth(t.Username, ipAddr, common.ProtocolOIDC, t.CustomFields)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
user = &u
|
||||
}
|
||||
if err := common.Config.ExecutePostConnectHook(ipAddr, common.ProtocolOIDC); err != nil {
|
||||
updateLoginMetrics(&user, dataprovider.LoginMethodIDP, ipAddr, err)
|
||||
updateLoginMetrics(user, dataprovider.LoginMethodIDP, ipAddr, err)
|
||||
return fmt.Errorf("access denied: %w", err)
|
||||
}
|
||||
if err := user.CheckLoginConditions(); err != nil {
|
||||
updateLoginMetrics(&user, dataprovider.LoginMethodIDP, ipAddr, err)
|
||||
updateLoginMetrics(user, dataprovider.LoginMethodIDP, ipAddr, err)
|
||||
return err
|
||||
}
|
||||
connectionID := fmt.Sprintf("%v_%v", common.ProtocolOIDC, xid.New().String())
|
||||
if err := checkHTTPClientUser(&user, r, connectionID, true); err != nil {
|
||||
updateLoginMetrics(&user, dataprovider.LoginMethodIDP, ipAddr, err)
|
||||
connectionID := fmt.Sprintf("%s_%s", common.ProtocolOIDC, xid.New().String())
|
||||
if err := checkHTTPClientUser(user, r, connectionID, true); err != nil {
|
||||
updateLoginMetrics(user, dataprovider.LoginMethodIDP, ipAddr, err)
|
||||
return err
|
||||
}
|
||||
defer user.CloseFs() //nolint:errcheck
|
||||
err = user.CheckFsRoot(connectionID)
|
||||
if err != nil {
|
||||
logger.Warn(logSender, connectionID, "unable to check fs root: %v", err)
|
||||
updateLoginMetrics(&user, dataprovider.LoginMethodIDP, ipAddr, common.ErrInternalFailure)
|
||||
updateLoginMetrics(user, dataprovider.LoginMethodIDP, ipAddr, common.ErrInternalFailure)
|
||||
return err
|
||||
}
|
||||
updateLoginMetrics(&user, dataprovider.LoginMethodIDP, ipAddr, nil)
|
||||
dataprovider.UpdateLastLogin(&user)
|
||||
updateLoginMetrics(user, dataprovider.LoginMethodIDP, ipAddr, nil)
|
||||
dataprovider.UpdateLastLogin(user)
|
||||
t.Permissions = user.Filters.WebClient
|
||||
t.TokenRole = user.Role
|
||||
return nil
|
||||
|
|
|
@ -66,15 +66,15 @@ type mockOAuth2Config struct {
|
|||
err error
|
||||
}
|
||||
|
||||
func (c *mockOAuth2Config) AuthCodeURL(state string, opts ...oauth2.AuthCodeOption) string {
|
||||
func (c *mockOAuth2Config) AuthCodeURL(_ string, _ ...oauth2.AuthCodeOption) string {
|
||||
return c.authCodeURL
|
||||
}
|
||||
|
||||
func (c *mockOAuth2Config) Exchange(ctx context.Context, code string, opts ...oauth2.AuthCodeOption) (*oauth2.Token, error) {
|
||||
func (c *mockOAuth2Config) Exchange(_ context.Context, _ string, _ ...oauth2.AuthCodeOption) (*oauth2.Token, error) {
|
||||
return c.token, c.err
|
||||
}
|
||||
|
||||
func (c *mockOAuth2Config) TokenSource(ctx context.Context, t *oauth2.Token) oauth2.TokenSource {
|
||||
func (c *mockOAuth2Config) TokenSource(_ context.Context, _ *oauth2.Token) oauth2.TokenSource {
|
||||
return c.tokenSource
|
||||
}
|
||||
|
||||
|
@ -83,7 +83,7 @@ type mockOIDCVerifier struct {
|
|||
err error
|
||||
}
|
||||
|
||||
func (v *mockOIDCVerifier) Verify(ctx context.Context, rawIDToken string) (*oidc.IDToken, error) {
|
||||
func (v *mockOIDCVerifier) Verify(_ context.Context, _ string) (*oidc.IDToken, error) {
|
||||
return v.token, v.err
|
||||
}
|
||||
|
||||
|
@ -1130,6 +1130,180 @@ func TestMemoryOIDCManager(t *testing.T) {
|
|||
require.Len(t, oidcMgr.tokens, 0)
|
||||
}
|
||||
|
||||
func TestOIDCEvMgrIntegration(t *testing.T) {
|
||||
providerConf := dataprovider.GetProviderConfig()
|
||||
err := dataprovider.Close()
|
||||
assert.NoError(t, err)
|
||||
newProviderConf := providerConf
|
||||
newProviderConf.NamingRules = 5
|
||||
err = dataprovider.Initialize(newProviderConf, configDir, true)
|
||||
assert.NoError(t, err)
|
||||
// add a special chars to check json replacer
|
||||
username := `test_"oidc_eventmanager`
|
||||
u := map[string]any{
|
||||
"username": "{{Name}}",
|
||||
"status": 1,
|
||||
"home_dir": filepath.Join(os.TempDir(), "{{IDPFieldcustom1}}"),
|
||||
"permissions": map[string][]string{
|
||||
"/": {dataprovider.PermAny},
|
||||
},
|
||||
}
|
||||
userTmpl, err := json.Marshal(u)
|
||||
require.NoError(t, err)
|
||||
a := map[string]any{
|
||||
"username": "{{Name}}",
|
||||
"status": 1,
|
||||
"permissions": []string{dataprovider.PermAdminAny},
|
||||
}
|
||||
adminTmpl, err := json.Marshal(a)
|
||||
require.NoError(t, err)
|
||||
|
||||
action := &dataprovider.BaseEventAction{
|
||||
Name: "a",
|
||||
Type: dataprovider.ActionTypeIDPAccountCheck,
|
||||
Options: dataprovider.BaseEventActionOptions{
|
||||
IDPConfig: dataprovider.EventActionIDPAccountCheck{
|
||||
Mode: 0,
|
||||
TemplateUser: string(userTmpl),
|
||||
TemplateAdmin: string(adminTmpl),
|
||||
},
|
||||
},
|
||||
}
|
||||
err = dataprovider.AddEventAction(action, "", "", "")
|
||||
assert.NoError(t, err)
|
||||
rule := &dataprovider.EventRule{
|
||||
Name: "r",
|
||||
Status: 1,
|
||||
Trigger: dataprovider.EventTriggerIDPLogin,
|
||||
Conditions: dataprovider.EventConditions{
|
||||
IDPLoginEvent: 0,
|
||||
},
|
||||
Actions: []dataprovider.EventAction{
|
||||
{
|
||||
BaseEventAction: dataprovider.BaseEventAction{
|
||||
Name: action.Name,
|
||||
},
|
||||
Options: dataprovider.EventActionOptions{
|
||||
ExecuteSync: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
err = dataprovider.AddEventRule(rule, "", "", "")
|
||||
assert.NoError(t, err)
|
||||
|
||||
oidcMgr, ok := oidcMgr.(*memoryOIDCManager)
|
||||
require.True(t, ok)
|
||||
server := getTestOIDCServer()
|
||||
server.binding.OIDC.ImplicitRoles = true
|
||||
server.binding.OIDC.CustomFields = []string{"custom1", "custom2"}
|
||||
err = server.binding.OIDC.initialize()
|
||||
assert.NoError(t, err)
|
||||
server.initializeRouter()
|
||||
// login a user with OIDC
|
||||
_, err = dataprovider.UserExists(username, "")
|
||||
assert.ErrorIs(t, err, util.ErrNotFound)
|
||||
authReq := newOIDCPendingAuth(tokenAudienceWebClient)
|
||||
oidcMgr.addPendingAuth(authReq)
|
||||
token := &oauth2.Token{
|
||||
AccessToken: "1234",
|
||||
Expiry: time.Now().Add(5 * time.Minute),
|
||||
}
|
||||
token = token.WithExtra(map[string]any{
|
||||
"id_token": "id_token_val",
|
||||
})
|
||||
server.binding.OIDC.oauth2Config = &mockOAuth2Config{
|
||||
tokenSource: &mockTokenSource{},
|
||||
authCodeURL: webOIDCRedirectPath,
|
||||
token: token,
|
||||
}
|
||||
idToken := &oidc.IDToken{
|
||||
Nonce: authReq.Nonce,
|
||||
Expiry: time.Now().Add(5 * time.Minute),
|
||||
}
|
||||
setIDTokenClaims(idToken, []byte(`{"preferred_username":"`+util.JSONEscape(username)+`","custom1":"val1"}`))
|
||||
server.binding.OIDC.verifier = &mockOIDCVerifier{
|
||||
err: nil,
|
||||
token: idToken,
|
||||
}
|
||||
rr := httptest.NewRecorder()
|
||||
r, err := http.NewRequest(http.MethodGet, webOIDCRedirectPath+"?state="+authReq.State, nil)
|
||||
assert.NoError(t, err)
|
||||
server.router.ServeHTTP(rr, r)
|
||||
assert.Equal(t, http.StatusFound, rr.Code)
|
||||
assert.Equal(t, webClientFilesPath, rr.Header().Get("Location"))
|
||||
user, err := dataprovider.UserExists(username, "")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, filepath.Join(os.TempDir(), "val1"), user.GetHomeDir())
|
||||
|
||||
err = dataprovider.DeleteUser(username, "", "", "")
|
||||
assert.NoError(t, err)
|
||||
err = os.RemoveAll(user.GetHomeDir())
|
||||
assert.NoError(t, err)
|
||||
// login an admin with OIDC
|
||||
_, err = dataprovider.AdminExists(username)
|
||||
assert.ErrorIs(t, err, util.ErrNotFound)
|
||||
authReq = newOIDCPendingAuth(tokenAudienceWebAdmin)
|
||||
oidcMgr.addPendingAuth(authReq)
|
||||
idToken = &oidc.IDToken{
|
||||
Nonce: authReq.Nonce,
|
||||
Expiry: time.Now().Add(5 * time.Minute),
|
||||
}
|
||||
setIDTokenClaims(idToken, []byte(`{"preferred_username":"`+util.JSONEscape(username)+`"}`))
|
||||
server.binding.OIDC.verifier = &mockOIDCVerifier{
|
||||
err: nil,
|
||||
token: idToken,
|
||||
}
|
||||
rr = httptest.NewRecorder()
|
||||
r, err = http.NewRequest(http.MethodGet, webOIDCRedirectPath+"?state="+authReq.State, nil)
|
||||
assert.NoError(t, err)
|
||||
server.router.ServeHTTP(rr, r)
|
||||
assert.Equal(t, http.StatusFound, rr.Code)
|
||||
assert.Equal(t, webUsersPath, rr.Header().Get("Location"))
|
||||
|
||||
_, err = dataprovider.AdminExists(username)
|
||||
assert.NoError(t, err)
|
||||
err = dataprovider.DeleteAdmin(username, "", "", "")
|
||||
assert.NoError(t, err)
|
||||
// set invalid templates and try again
|
||||
action.Options.IDPConfig.TemplateUser = `{}`
|
||||
action.Options.IDPConfig.TemplateAdmin = `{}`
|
||||
err = dataprovider.UpdateEventAction(action, "", "", "")
|
||||
assert.NoError(t, err)
|
||||
|
||||
for _, audience := range []string{tokenAudienceWebAdmin, tokenAudienceWebClient} {
|
||||
authReq = newOIDCPendingAuth(audience)
|
||||
oidcMgr.addPendingAuth(authReq)
|
||||
idToken = &oidc.IDToken{
|
||||
Nonce: authReq.Nonce,
|
||||
Expiry: time.Now().Add(5 * time.Minute),
|
||||
}
|
||||
setIDTokenClaims(idToken, []byte(`{"preferred_username":"`+util.JSONEscape(username)+`"}`))
|
||||
server.binding.OIDC.verifier = &mockOIDCVerifier{
|
||||
err: nil,
|
||||
token: idToken,
|
||||
}
|
||||
rr = httptest.NewRecorder()
|
||||
r, err = http.NewRequest(http.MethodGet, webOIDCRedirectPath+"?state="+authReq.State, nil)
|
||||
assert.NoError(t, err)
|
||||
server.router.ServeHTTP(rr, r)
|
||||
assert.Equal(t, http.StatusFound, rr.Code)
|
||||
}
|
||||
for k := range oidcMgr.tokens {
|
||||
oidcMgr.removeToken(k)
|
||||
}
|
||||
|
||||
err = dataprovider.DeleteEventRule(rule.Name, "", "", "")
|
||||
assert.NoError(t, err)
|
||||
err = dataprovider.DeleteEventAction(action.Name, "", "", "")
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = dataprovider.Close()
|
||||
assert.NoError(t, err)
|
||||
err = dataprovider.Initialize(providerConf, configDir, true)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestOIDCPreLoginHook(t *testing.T) {
|
||||
if runtime.GOOS == osWindows {
|
||||
t.Skip("this test is not available on Windows")
|
||||
|
|
|
@ -2280,6 +2280,10 @@ func getEventActionOptionsFromPostFields(r *http.Request) (dataprovider.BaseEven
|
|||
if r.Form.Get("cmd_arguments") != "" {
|
||||
cmdArgs = getSliceFromDelimitedValues(r.Form.Get("cmd_arguments"), ",")
|
||||
}
|
||||
idpMode := 0
|
||||
if r.Form.Get("idp_mode") == "1" {
|
||||
idpMode = 1
|
||||
}
|
||||
options := dataprovider.BaseEventActionOptions{
|
||||
HTTPConfig: dataprovider.EventActionHTTPConfig{
|
||||
Endpoint: r.Form.Get("http_endpoint"),
|
||||
|
@ -2323,6 +2327,11 @@ func getEventActionOptionsFromPostFields(r *http.Request) (dataprovider.BaseEven
|
|||
PwdExpirationConfig: dataprovider.EventActionPasswordExpiration{
|
||||
Threshold: pwdExpirationThreshold,
|
||||
},
|
||||
IDPConfig: dataprovider.EventActionIDPAccountCheck{
|
||||
Mode: idpMode,
|
||||
TemplateUser: strings.TrimSpace(r.Form.Get("idp_user")),
|
||||
TemplateAdmin: strings.TrimSpace(r.Form.Get("idp_admin")),
|
||||
},
|
||||
}
|
||||
return options, nil
|
||||
}
|
||||
|
@ -2349,6 +2358,17 @@ func getEventActionFromPostFields(r *http.Request) (dataprovider.BaseEventAction
|
|||
return action, nil
|
||||
}
|
||||
|
||||
func getIDPLoginEventFromPostField(r *http.Request) int {
|
||||
switch r.Form.Get("idp_login_event") {
|
||||
case "1":
|
||||
return 1
|
||||
case "2":
|
||||
return 2
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func getEventRuleConditionsFromPostFields(r *http.Request) (dataprovider.EventConditions, error) {
|
||||
var schedules []dataprovider.Schedule
|
||||
var names, groupNames, roleNames, fsPaths []dataprovider.ConditionPattern
|
||||
|
@ -2424,6 +2444,7 @@ func getEventRuleConditionsFromPostFields(r *http.Request) (dataprovider.EventCo
|
|||
conditions := dataprovider.EventConditions{
|
||||
FsEvents: r.Form["fs_events"],
|
||||
ProviderEvents: r.Form["provider_events"],
|
||||
IDPLoginEvent: getIDPLoginEventFromPostField(r),
|
||||
Schedules: schedules,
|
||||
Options: dataprovider.ConditionOptions{
|
||||
Names: names,
|
||||
|
|
|
@ -1596,6 +1596,9 @@ func checkEventAction(expected, actual dataprovider.BaseEventAction) error {
|
|||
if expected.Options.PwdExpirationConfig.Threshold != actual.Options.PwdExpirationConfig.Threshold {
|
||||
return errors.New("password expiration threshold mismatch")
|
||||
}
|
||||
if err := compareEventActionIDPConfigFields(expected.Options.IDPConfig, actual.Options.IDPConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := compareEventActionCmdConfigFields(expected.Options.CmdConfig, actual.Options.CmdConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1707,6 +1710,9 @@ func checkEventConditions(expected, actual dataprovider.EventConditions) error {
|
|||
if err := checkEventConditionOptions(expected.Options, actual.Options); err != nil {
|
||||
return err
|
||||
}
|
||||
if expected.IDPLoginEvent != actual.IDPLoginEvent {
|
||||
return errors.New("IDP login event mismatch")
|
||||
}
|
||||
|
||||
return checkEventSchedules(expected.Schedules, actual.Schedules)
|
||||
}
|
||||
|
@ -2707,6 +2713,19 @@ func compareEventActionFsConfigFields(expected, actual dataprovider.EventActionF
|
|||
return compareEventActionFsCompressFields(expected.Compress, actual.Compress)
|
||||
}
|
||||
|
||||
func compareEventActionIDPConfigFields(expected, actual dataprovider.EventActionIDPAccountCheck) error {
|
||||
if expected.Mode != actual.Mode {
|
||||
return errors.New("mode mismatch")
|
||||
}
|
||||
if expected.TemplateAdmin != actual.TemplateAdmin {
|
||||
return errors.New("admin template mismatch")
|
||||
}
|
||||
if expected.TemplateUser != actual.TemplateUser {
|
||||
return errors.New("user template mismatch")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func compareEventActionCmdConfigFields(expected, actual dataprovider.EventActionCommandConfig) error {
|
||||
if expected.Cmd != actual.Cmd {
|
||||
return errors.New("command mismatch")
|
||||
|
|
|
@ -38,7 +38,7 @@ func init() {
|
|||
RegisterSecretProvider(sdkkms.SchemeBuiltin, sdkkms.SecretStatusAES256GCM, newBuiltinSecret)
|
||||
}
|
||||
|
||||
func newBuiltinSecret(base BaseSecret, url, masterKey string) SecretProvider {
|
||||
func newBuiltinSecret(base BaseSecret, _, _ string) SecretProvider {
|
||||
return &builtinSecret{
|
||||
BaseSecret: base,
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ type localSecret struct {
|
|||
}
|
||||
|
||||
// NewLocalSecret returns a SecretProvider that use a locally provided symmetric key
|
||||
func NewLocalSecret(base BaseSecret, url, masterKey string) SecretProvider {
|
||||
func NewLocalSecret(base BaseSecret, _, masterKey string) SecretProvider {
|
||||
return &localSecret{
|
||||
BaseSecret: base,
|
||||
masterKey: masterKey,
|
||||
|
|
|
@ -81,11 +81,11 @@ func (l *HCLogAdapter) Named(name string) hclog.Logger {
|
|||
}
|
||||
|
||||
// StandardLogger returns a value that conforms to the stdlib log.Logger interface
|
||||
func (l *HCLogAdapter) StandardLogger(opts *hclog.StandardLoggerOptions) *log.Logger {
|
||||
func (l *HCLogAdapter) StandardLogger(_ *hclog.StandardLoggerOptions) *log.Logger {
|
||||
return log.New(&StdLoggerWrapper{Sender: l.Name()}, "", 0)
|
||||
}
|
||||
|
||||
// StandardWriter returns a value that conforms to io.Writer, which can be passed into log.SetOutput()
|
||||
func (l *HCLogAdapter) StandardWriter(opts *hclog.StandardLoggerOptions) io.Writer {
|
||||
func (l *HCLogAdapter) StandardWriter(_ *hclog.StandardLoggerOptions) io.Writer {
|
||||
return &StdLoggerWrapper{Sender: l.Name()}
|
||||
}
|
||||
|
|
|
@ -71,7 +71,7 @@ func (l *StructuredLogger) NewLogEntry(r *http.Request) middleware.LogEntry {
|
|||
}
|
||||
|
||||
// Write logs a new entry at the end of the HTTP request
|
||||
func (l *StructuredLoggerEntry) Write(status, bytes int, header http.Header, elapsed time.Duration, extra any) {
|
||||
func (l *StructuredLoggerEntry) Write(status, bytes int, _ http.Header, elapsed time.Duration, _ any) {
|
||||
metric.HTTPRequestServed(status)
|
||||
l.Logger.Info().
|
||||
Timestamp().
|
||||
|
|
|
@ -338,7 +338,7 @@ func (m *Manager) SetModificationTime(storageID, objectPath string, mTime int64)
|
|||
}
|
||||
|
||||
// GetModificationTime returns the modification time for the specified path
|
||||
func (m *Manager) GetModificationTime(storageID, objectPath string, isDir bool) (int64, error) {
|
||||
func (m *Manager) GetModificationTime(storageID, objectPath string, _ bool) (int64, error) {
|
||||
if !m.hasMetadater {
|
||||
return 0, ErrNoMetadater
|
||||
}
|
||||
|
|
|
@ -351,7 +351,7 @@ func (s *Service) LoadInitialData() error {
|
|||
}
|
||||
|
||||
func (s *Service) restoreDump(dump *dataprovider.BackupData) error {
|
||||
err := httpd.RestoreConfigs(dump.Configs, s.LoadDataFrom, s.LoadDataMode, dataprovider.ActionExecutorSystem, "", "")
|
||||
err := httpd.RestoreConfigs(dump.Configs, s.LoadDataMode, dataprovider.ActionExecutorSystem, "", "")
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to restore configs from file %q: %v", s.LoadDataFrom, err)
|
||||
}
|
||||
|
|
|
@ -83,7 +83,7 @@ func (c *MockChannel) CloseWrite() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *MockChannel) SendRequest(name string, wantReply bool, payload []byte) (bool, error) {
|
||||
func (c *MockChannel) SendRequest(_ string, _ bool, _ []byte) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
|
@ -131,7 +131,7 @@ func (fs MockOsFs) Lstat(name string) (os.FileInfo, error) {
|
|||
}
|
||||
|
||||
// Remove removes the named file or (empty) directory.
|
||||
func (fs MockOsFs) Remove(name string, isDir bool) error {
|
||||
func (fs MockOsFs) Remove(name string, _ bool) error {
|
||||
if fs.err != nil {
|
||||
return fs.err
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ type failingReader struct {
|
|||
errRead error
|
||||
}
|
||||
|
||||
func (r *failingReader) ReadAt(p []byte, off int64) (n int, err error) {
|
||||
func (r *failingReader) ReadAt(_ []byte, _ int64) (n int, err error) {
|
||||
return 0, r.errRead
|
||||
}
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ import (
|
|||
"github.com/drakkan/sftpgo/v2/internal/dataprovider"
|
||||
"github.com/drakkan/sftpgo/v2/internal/logger"
|
||||
"github.com/drakkan/sftpgo/v2/internal/util"
|
||||
"github.com/drakkan/sftpgo/v2/internal/version"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -287,7 +288,9 @@ func (c *Config) getMailClientOptions() []mail.Option {
|
|||
|
||||
func (c *Config) getSMTPClientAndMsg(to []string, subject, body string, contentType EmailContentType,
|
||||
attachments ...*mail.File) (*mail.Client, *mail.Msg, error) {
|
||||
version := version.Get()
|
||||
msg := mail.NewMsg()
|
||||
msg.SetUserAgent(fmt.Sprintf("SFTPGo-%s-%s", version.Version, version.CommitHash))
|
||||
|
||||
var from string
|
||||
if c.From != "" {
|
||||
|
|
|
@ -24,6 +24,7 @@ import (
|
|||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
@ -589,7 +590,7 @@ func HTTPListenAndServe(srv *http.Server, address string, port int, isTLS bool,
|
|||
return err
|
||||
}
|
||||
|
||||
logger.Info(logSender, "", "server listener registered, address: %v TLS enabled: %v", listener.Addr().String(), isTLS)
|
||||
logger.Info(logSender, "", "server listener registered, address: %s TLS enabled: %t", listener.Addr().String(), isTLS)
|
||||
|
||||
defer listener.Close()
|
||||
|
||||
|
@ -834,3 +835,15 @@ func GetLastIPForPrefix(p netip.Prefix) netip.Addr {
|
|||
}
|
||||
return netip.AddrFrom16(a16) // doesn't unmap
|
||||
}
|
||||
|
||||
// JSONEscape returns the JSON escaped format for the input string
|
||||
func JSONEscape(val string) string {
|
||||
if val == "" {
|
||||
return val
|
||||
}
|
||||
b, err := json.Marshal(val)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return string(b[1 : len(b)-1])
|
||||
}
|
||||
|
|
|
@ -326,27 +326,27 @@ func (fs *AzureBlobFs) Mkdir(name string) error {
|
|||
}
|
||||
|
||||
// Symlink creates source as a symbolic link to target.
|
||||
func (*AzureBlobFs) Symlink(source, target string) error {
|
||||
func (*AzureBlobFs) Symlink(_, _ string) error {
|
||||
return ErrVfsUnsupported
|
||||
}
|
||||
|
||||
// Readlink returns the destination of the named symbolic link
|
||||
func (*AzureBlobFs) Readlink(name string) (string, error) {
|
||||
func (*AzureBlobFs) Readlink(_ string) (string, error) {
|
||||
return "", ErrVfsUnsupported
|
||||
}
|
||||
|
||||
// Chown changes the numeric uid and gid of the named file.
|
||||
func (*AzureBlobFs) Chown(name string, uid int, gid int) error {
|
||||
func (*AzureBlobFs) Chown(_ string, _ int, _ int) error {
|
||||
return ErrVfsUnsupported
|
||||
}
|
||||
|
||||
// Chmod changes the mode of the named file to mode.
|
||||
func (*AzureBlobFs) Chmod(name string, mode os.FileMode) error {
|
||||
func (*AzureBlobFs) Chmod(_ string, _ os.FileMode) error {
|
||||
return ErrVfsUnsupported
|
||||
}
|
||||
|
||||
// Chtimes changes the access and modification times of the named file.
|
||||
func (fs *AzureBlobFs) Chtimes(name string, atime, mtime time.Time, isUploading bool) error {
|
||||
func (fs *AzureBlobFs) Chtimes(name string, _, mtime time.Time, isUploading bool) error {
|
||||
if !plugin.Handler.HasMetadater() {
|
||||
return ErrVfsUnsupported
|
||||
}
|
||||
|
@ -367,7 +367,7 @@ func (fs *AzureBlobFs) Chtimes(name string, atime, mtime time.Time, isUploading
|
|||
// Truncate changes the size of the named file.
|
||||
// Truncate by path is not supported, while truncating an opened
|
||||
// file is handled inside base transfer
|
||||
func (*AzureBlobFs) Truncate(name string, size int64) error {
|
||||
func (*AzureBlobFs) Truncate(_ string, _ int64) error {
|
||||
return ErrVfsUnsupported
|
||||
}
|
||||
|
||||
|
@ -609,7 +609,7 @@ func (fs *AzureBlobFs) GetDirSize(dirname string) (int, int64, error) {
|
|||
|
||||
// GetAtomicUploadPath returns the path to use for an atomic upload.
|
||||
// Azure Blob Storage uploads are already atomic, we never call this method
|
||||
func (*AzureBlobFs) GetAtomicUploadPath(name string) string {
|
||||
func (*AzureBlobFs) GetAtomicUploadPath(_ string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
|
@ -702,7 +702,7 @@ func (fs *AzureBlobFs) ResolvePath(virtualPath string) (string, error) {
|
|||
}
|
||||
|
||||
// CopyFile implements the FsFileCopier interface
|
||||
func (fs *AzureBlobFs) CopyFile(source, target string, srcSize int64) error {
|
||||
func (fs *AzureBlobFs) CopyFile(source, target string, _ int64) error {
|
||||
return fs.copyFileInternal(source, target)
|
||||
}
|
||||
|
||||
|
@ -731,7 +731,7 @@ func (*AzureBlobFs) Close() error {
|
|||
}
|
||||
|
||||
// GetAvailableDiskSize returns the available size for the specified path
|
||||
func (*AzureBlobFs) GetAvailableDiskSize(dirName string) (*sftp.StatVFS, error) {
|
||||
func (*AzureBlobFs) GetAvailableDiskSize(_ string) (*sftp.StatVFS, error) {
|
||||
return nil, ErrStorageSizeUnavailable
|
||||
}
|
||||
|
||||
|
|
|
@ -206,7 +206,7 @@ func (fs *CryptFs) Create(name string, flag int) (File, *PipeWriter, func(), err
|
|||
}
|
||||
|
||||
// Truncate changes the size of the named file
|
||||
func (*CryptFs) Truncate(name string, size int64) error {
|
||||
func (*CryptFs) Truncate(_ string, _ int64) error {
|
||||
return ErrVfsUnsupported
|
||||
}
|
||||
|
||||
|
|
|
@ -284,27 +284,27 @@ func (fs *GCSFs) Mkdir(name string) error {
|
|||
}
|
||||
|
||||
// Symlink creates source as a symbolic link to target.
|
||||
func (*GCSFs) Symlink(source, target string) error {
|
||||
func (*GCSFs) Symlink(_, _ string) error {
|
||||
return ErrVfsUnsupported
|
||||
}
|
||||
|
||||
// Readlink returns the destination of the named symbolic link
|
||||
func (*GCSFs) Readlink(name string) (string, error) {
|
||||
func (*GCSFs) Readlink(_ string) (string, error) {
|
||||
return "", ErrVfsUnsupported
|
||||
}
|
||||
|
||||
// Chown changes the numeric uid and gid of the named file.
|
||||
func (*GCSFs) Chown(name string, uid int, gid int) error {
|
||||
func (*GCSFs) Chown(_ string, _ int, _ int) error {
|
||||
return ErrVfsUnsupported
|
||||
}
|
||||
|
||||
// Chmod changes the mode of the named file to mode.
|
||||
func (*GCSFs) Chmod(name string, mode os.FileMode) error {
|
||||
func (*GCSFs) Chmod(_ string, _ os.FileMode) error {
|
||||
return ErrVfsUnsupported
|
||||
}
|
||||
|
||||
// Chtimes changes the access and modification times of the named file.
|
||||
func (fs *GCSFs) Chtimes(name string, atime, mtime time.Time, isUploading bool) error {
|
||||
func (fs *GCSFs) Chtimes(name string, _, mtime time.Time, isUploading bool) error {
|
||||
if !plugin.Handler.HasMetadater() {
|
||||
return ErrVfsUnsupported
|
||||
}
|
||||
|
@ -325,7 +325,7 @@ func (fs *GCSFs) Chtimes(name string, atime, mtime time.Time, isUploading bool)
|
|||
// Truncate changes the size of the named file.
|
||||
// Truncate by path is not supported, while truncating an opened
|
||||
// file is handled inside base transfer
|
||||
func (*GCSFs) Truncate(name string, size int64) error {
|
||||
func (*GCSFs) Truncate(_ string, _ int64) error {
|
||||
return ErrVfsUnsupported
|
||||
}
|
||||
|
||||
|
@ -587,7 +587,7 @@ func (fs *GCSFs) GetDirSize(dirname string) (int, int64, error) {
|
|||
|
||||
// GetAtomicUploadPath returns the path to use for an atomic upload.
|
||||
// GCS uploads are already atomic, we never call this method for GCS
|
||||
func (*GCSFs) GetAtomicUploadPath(name string) string {
|
||||
func (*GCSFs) GetAtomicUploadPath(_ string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
|
@ -688,7 +688,7 @@ func (fs *GCSFs) ResolvePath(virtualPath string) (string, error) {
|
|||
}
|
||||
|
||||
// CopyFile implements the FsFileCopier interface
|
||||
func (fs *GCSFs) CopyFile(source, target string, srcSize int64) error {
|
||||
func (fs *GCSFs) CopyFile(source, target string, _ int64) error {
|
||||
return fs.copyFileInternal(source, target)
|
||||
}
|
||||
|
||||
|
@ -904,7 +904,7 @@ func (fs *GCSFs) Close() error {
|
|||
}
|
||||
|
||||
// GetAvailableDiskSize returns the available size for the specified path
|
||||
func (*GCSFs) GetAvailableDiskSize(dirName string) (*sftp.StatVFS, error) {
|
||||
func (*GCSFs) GetAvailableDiskSize(_ string) (*sftp.StatVFS, error) {
|
||||
return nil, ErrStorageSizeUnavailable
|
||||
}
|
||||
|
||||
|
|
|
@ -385,7 +385,7 @@ func (fs *HTTPFs) Rename(source, target string) (int, int64, error) {
|
|||
}
|
||||
|
||||
// Remove removes the named file or (empty) directory.
|
||||
func (fs *HTTPFs) Remove(name string, isDir bool) error {
|
||||
func (fs *HTTPFs) Remove(name string, _ bool) error {
|
||||
ctx, cancelFn := context.WithDeadline(context.Background(), time.Now().Add(fs.ctxTimeout))
|
||||
defer cancelFn()
|
||||
|
||||
|
@ -411,17 +411,17 @@ func (fs *HTTPFs) Mkdir(name string) error {
|
|||
}
|
||||
|
||||
// Symlink creates source as a symbolic link to target.
|
||||
func (*HTTPFs) Symlink(source, target string) error {
|
||||
func (*HTTPFs) Symlink(_, _ string) error {
|
||||
return ErrVfsUnsupported
|
||||
}
|
||||
|
||||
// Readlink returns the destination of the named symbolic link
|
||||
func (*HTTPFs) Readlink(name string) (string, error) {
|
||||
func (*HTTPFs) Readlink(_ string) (string, error) {
|
||||
return "", ErrVfsUnsupported
|
||||
}
|
||||
|
||||
// Chown changes the numeric uid and gid of the named file.
|
||||
func (fs *HTTPFs) Chown(name string, uid int, gid int) error {
|
||||
func (fs *HTTPFs) Chown(_ string, _ int, _ int) error {
|
||||
return ErrVfsUnsupported
|
||||
}
|
||||
|
||||
|
@ -440,7 +440,7 @@ func (fs *HTTPFs) Chmod(name string, mode os.FileMode) error {
|
|||
}
|
||||
|
||||
// Chtimes changes the access and modification times of the named file.
|
||||
func (fs *HTTPFs) Chtimes(name string, atime, mtime time.Time, isUploading bool) error {
|
||||
func (fs *HTTPFs) Chtimes(name string, atime, mtime time.Time, _ bool) error {
|
||||
ctx, cancelFn := context.WithDeadline(context.Background(), time.Now().Add(fs.ctxTimeout))
|
||||
defer cancelFn()
|
||||
|
||||
|
@ -562,7 +562,7 @@ func (fs *HTTPFs) GetDirSize(dirname string) (int, int64, error) {
|
|||
}
|
||||
|
||||
// GetAtomicUploadPath returns the path to use for an atomic upload.
|
||||
func (*HTTPFs) GetAtomicUploadPath(name string) string {
|
||||
func (*HTTPFs) GetAtomicUploadPath(_ string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
|
|
|
@ -140,7 +140,7 @@ func (fs *OsFs) Rename(source, target string) (int, int64, error) {
|
|||
}
|
||||
|
||||
// Remove removes the named file or (empty) directory.
|
||||
func (*OsFs) Remove(name string, isDir bool) error {
|
||||
func (*OsFs) Remove(name string, _ bool) error {
|
||||
return os.Remove(name)
|
||||
}
|
||||
|
||||
|
@ -181,7 +181,7 @@ func (*OsFs) Chmod(name string, mode os.FileMode) error {
|
|||
}
|
||||
|
||||
// Chtimes changes the access and modification times of the named file
|
||||
func (*OsFs) Chtimes(name string, atime, mtime time.Time, isUploading bool) error {
|
||||
func (*OsFs) Chtimes(name string, atime, mtime time.Time, _ bool) error {
|
||||
return os.Chtimes(name, atime, mtime)
|
||||
}
|
||||
|
||||
|
|
|
@ -337,27 +337,27 @@ func (fs *S3Fs) Mkdir(name string) error {
|
|||
}
|
||||
|
||||
// Symlink creates source as a symbolic link to target.
|
||||
func (*S3Fs) Symlink(source, target string) error {
|
||||
func (*S3Fs) Symlink(_, _ string) error {
|
||||
return ErrVfsUnsupported
|
||||
}
|
||||
|
||||
// Readlink returns the destination of the named symbolic link
|
||||
func (*S3Fs) Readlink(name string) (string, error) {
|
||||
func (*S3Fs) Readlink(_ string) (string, error) {
|
||||
return "", ErrVfsUnsupported
|
||||
}
|
||||
|
||||
// Chown changes the numeric uid and gid of the named file.
|
||||
func (*S3Fs) Chown(name string, uid int, gid int) error {
|
||||
func (*S3Fs) Chown(_ string, _ int, _ int) error {
|
||||
return ErrVfsUnsupported
|
||||
}
|
||||
|
||||
// Chmod changes the mode of the named file to mode.
|
||||
func (*S3Fs) Chmod(name string, mode os.FileMode) error {
|
||||
func (*S3Fs) Chmod(_ string, _ os.FileMode) error {
|
||||
return ErrVfsUnsupported
|
||||
}
|
||||
|
||||
// Chtimes changes the access and modification times of the named file.
|
||||
func (fs *S3Fs) Chtimes(name string, atime, mtime time.Time, isUploading bool) error {
|
||||
func (fs *S3Fs) Chtimes(name string, _, mtime time.Time, isUploading bool) error {
|
||||
if !plugin.Handler.HasMetadater() {
|
||||
return ErrVfsUnsupported
|
||||
}
|
||||
|
@ -377,7 +377,7 @@ func (fs *S3Fs) Chtimes(name string, atime, mtime time.Time, isUploading bool) e
|
|||
// Truncate changes the size of the named file.
|
||||
// Truncate by path is not supported, while truncating an opened
|
||||
// file is handled inside base transfer
|
||||
func (*S3Fs) Truncate(name string, size int64) error {
|
||||
func (*S3Fs) Truncate(_ string, _ int64) error {
|
||||
return ErrVfsUnsupported
|
||||
}
|
||||
|
||||
|
@ -595,7 +595,7 @@ func (fs *S3Fs) GetDirSize(dirname string) (int, int64, error) {
|
|||
|
||||
// GetAtomicUploadPath returns the path to use for an atomic upload.
|
||||
// S3 uploads are already atomic, we never call this method for S3
|
||||
func (*S3Fs) GetAtomicUploadPath(name string) string {
|
||||
func (*S3Fs) GetAtomicUploadPath(_ string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
|
@ -1009,7 +1009,7 @@ func (*S3Fs) Close() error {
|
|||
}
|
||||
|
||||
// GetAvailableDiskSize returns the available size for the specified path
|
||||
func (*S3Fs) GetAvailableDiskSize(dirName string) (*sftp.StatVFS, error) {
|
||||
func (*S3Fs) GetAvailableDiskSize(_ string) (*sftp.StatVFS, error) {
|
||||
return nil, ErrStorageSizeUnavailable
|
||||
}
|
||||
|
||||
|
|
|
@ -510,7 +510,7 @@ func (fs *SFTPFs) Chmod(name string, mode os.FileMode) error {
|
|||
}
|
||||
|
||||
// Chtimes changes the access and modification times of the named file.
|
||||
func (fs *SFTPFs) Chtimes(name string, atime, mtime time.Time, isUploading bool) error {
|
||||
func (fs *SFTPFs) Chtimes(name string, atime, mtime time.Time, _ bool) error {
|
||||
client, err := fs.conn.getClient()
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -27,6 +27,6 @@ func isCrossDeviceError(err error) bool {
|
|||
return errors.Is(err, unix.EXDEV)
|
||||
}
|
||||
|
||||
func isInvalidNameError(err error) bool {
|
||||
func isInvalidNameError(_ error) bool {
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -82,7 +82,7 @@ type webDavFileInfo struct {
|
|||
}
|
||||
|
||||
// ContentType implements webdav.ContentTyper interface
|
||||
func (fi *webDavFileInfo) ContentType(ctx context.Context) (string, error) {
|
||||
func (fi *webDavFileInfo) ContentType(_ context.Context) (string, error) {
|
||||
extension := path.Ext(fi.virtualPath)
|
||||
if ctype, ok := customMimeTypeMapping[extension]; ok {
|
||||
return ctype, nil
|
||||
|
@ -107,7 +107,7 @@ func (fi *webDavFileInfo) ContentType(ctx context.Context) (string, error) {
|
|||
}
|
||||
|
||||
// Readdir reads directory entries from the handle
|
||||
func (f *webDavFile) Readdir(count int) ([]os.FileInfo, error) {
|
||||
func (f *webDavFile) Readdir(_ int) ([]os.FileInfo, error) {
|
||||
if !f.Connection.User.HasPerm(dataprovider.PermListItems, f.GetVirtualPath()) {
|
||||
return nil, f.Connection.GetPermissionDeniedError()
|
||||
}
|
||||
|
|
|
@ -85,7 +85,7 @@ func (c *Connection) GetCommand() string {
|
|||
}
|
||||
|
||||
// Mkdir creates a directory using the connection filesystem
|
||||
func (c *Connection) Mkdir(ctx context.Context, name string, perm os.FileMode) error {
|
||||
func (c *Connection) Mkdir(_ context.Context, name string, _ os.FileMode) error {
|
||||
c.UpdateLastActivity()
|
||||
|
||||
name = util.CleanPath(name)
|
||||
|
@ -93,7 +93,7 @@ func (c *Connection) Mkdir(ctx context.Context, name string, perm os.FileMode) e
|
|||
}
|
||||
|
||||
// Rename renames a file or a directory
|
||||
func (c *Connection) Rename(ctx context.Context, oldName, newName string) error {
|
||||
func (c *Connection) Rename(_ context.Context, oldName, newName string) error {
|
||||
c.UpdateLastActivity()
|
||||
|
||||
oldName = util.CleanPath(oldName)
|
||||
|
@ -116,7 +116,7 @@ func (c *Connection) Rename(ctx context.Context, oldName, newName string) error
|
|||
|
||||
// Stat returns a FileInfo describing the named file/directory, or an error,
|
||||
// if any happens
|
||||
func (c *Connection) Stat(ctx context.Context, name string) (os.FileInfo, error) {
|
||||
func (c *Connection) Stat(_ context.Context, name string) (os.FileInfo, error) {
|
||||
c.UpdateLastActivity()
|
||||
|
||||
name = util.CleanPath(name)
|
||||
|
@ -133,7 +133,7 @@ func (c *Connection) Stat(ctx context.Context, name string) (os.FileInfo, error)
|
|||
|
||||
// RemoveAll removes path and any children it contains.
|
||||
// If the path does not exist, RemoveAll returns nil (no error).
|
||||
func (c *Connection) RemoveAll(ctx context.Context, name string) error {
|
||||
func (c *Connection) RemoveAll(_ context.Context, name string) error {
|
||||
c.UpdateLastActivity()
|
||||
|
||||
name = util.CleanPath(name)
|
||||
|
@ -142,7 +142,7 @@ func (c *Connection) RemoveAll(ctx context.Context, name string) error {
|
|||
|
||||
// OpenFile opens the named file with specified flag.
|
||||
// This method is used for uploads and downloads but also for Stat and Readdir
|
||||
func (c *Connection) OpenFile(ctx context.Context, name string, flag int, perm os.FileMode) (webdav.File, error) {
|
||||
func (c *Connection) OpenFile(_ context.Context, name string, flag int, _ os.FileMode) (webdav.File, error) {
|
||||
c.UpdateLastActivity()
|
||||
|
||||
name = util.CleanPath(name)
|
||||
|
|
|
@ -303,7 +303,7 @@ func (fs *MockOsFs) IsAtomicUploadSupported() bool {
|
|||
}
|
||||
|
||||
// Remove removes the named file or (empty) directory.
|
||||
func (fs *MockOsFs) Remove(name string, isDir bool) error {
|
||||
func (fs *MockOsFs) Remove(name string, _ bool) error {
|
||||
if fs.err != nil {
|
||||
return fs.err
|
||||
}
|
||||
|
@ -317,7 +317,7 @@ func (fs *MockOsFs) Rename(source, target string) (int, int64, error) {
|
|||
}
|
||||
|
||||
// GetMimeType returns the content type
|
||||
func (fs *MockOsFs) GetMimeType(name string) (string, error) {
|
||||
func (fs *MockOsFs) GetMimeType(_ string) (string, error) {
|
||||
if fs.err != nil {
|
||||
return "", fs.err
|
||||
}
|
||||
|
|
|
@ -4874,6 +4874,7 @@ components:
|
|||
- 10
|
||||
- 11
|
||||
- 12
|
||||
- 13
|
||||
description: |
|
||||
Supported event action types:
|
||||
* `1` - HTTP
|
||||
|
@ -4888,6 +4889,7 @@ components:
|
|||
* `10` - Metadata check
|
||||
* `11` - Password expiration check
|
||||
* `12` - User expiration check
|
||||
* `13` - Identity Provider account check
|
||||
FilesystemActionTypes:
|
||||
type: integer
|
||||
enum:
|
||||
|
@ -4910,6 +4912,7 @@ components:
|
|||
- 4
|
||||
- 5
|
||||
- 6
|
||||
- 7
|
||||
description: |
|
||||
Supported event trigger types:
|
||||
* `1` - Filesystem event
|
||||
|
@ -4918,6 +4921,7 @@ components:
|
|||
* `4` - IP blocked
|
||||
* `5` - Certificate renewal
|
||||
* `6` - On demand, like schedule but executed on demand
|
||||
* `7` - Identity provider login
|
||||
LoginMethods:
|
||||
type: string
|
||||
enum:
|
||||
|
@ -6870,6 +6874,24 @@ components:
|
|||
threshold:
|
||||
type: integer
|
||||
description: 'An email notification will be generated for users whose password expires in a number of days less than or equal to this threshold'
|
||||
EventActionIDPAccountCheck:
|
||||
type: object
|
||||
properties:
|
||||
mode:
|
||||
type: integer
|
||||
enum:
|
||||
- 0
|
||||
- 1
|
||||
description: |
|
||||
Account check mode:
|
||||
* `0` Create or update the account
|
||||
* `1` Create the account if it doesn't exist
|
||||
template_user:
|
||||
type: string
|
||||
description: 'SFTPGo user template in JSON format'
|
||||
template_admin:
|
||||
type: string
|
||||
description: 'SFTPGo admin template in JSON format'
|
||||
BaseEventActionOptions:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -6885,6 +6907,8 @@ components:
|
|||
$ref: '#/components/schemas/EventActionFilesystemConfig'
|
||||
pwd_expiration_config:
|
||||
$ref: '#/components/schemas/EventActionPasswordExpiration'
|
||||
idp_config:
|
||||
$ref: '#/components/schemas/EventActionIDPAccountCheck'
|
||||
BaseEventAction:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -7026,6 +7050,8 @@ components:
|
|||
- pre-upload
|
||||
- pre-download
|
||||
- pre-delete
|
||||
- first-upload
|
||||
- first-download
|
||||
provider_events:
|
||||
type: array
|
||||
items:
|
||||
|
@ -7038,6 +7064,17 @@ components:
|
|||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Schedule'
|
||||
idp_login_event:
|
||||
type: integer
|
||||
enum:
|
||||
- 0
|
||||
- 1
|
||||
- 2
|
||||
description: |
|
||||
IDP login events:
|
||||
- `0` any login event
|
||||
- `1` user login event
|
||||
- `2` admin login event
|
||||
options:
|
||||
$ref: '#/components/schemas/ConditionOptions'
|
||||
BaseEventRule:
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/bash
|
||||
|
||||
NFPM_VERSION=2.26.0
|
||||
NFPM_VERSION=2.27.1
|
||||
NFPM_ARCH=${NFPM_ARCH:-amd64}
|
||||
if [ -z ${SFTPGO_VERSION} ]
|
||||
then
|
||||
|
|
|
@ -88,6 +88,38 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row action-type action-idp">
|
||||
<label for="idIDPMode" class="col-sm-2 col-form-label">Mode</label>
|
||||
<div class="col-sm-10">
|
||||
<select class="form-control selectpicker" id="idIDPMode" name="idp_mode">
|
||||
<option value="0" {{ if eq .Action.Options.IDPConfig.Mode 0 }}selected{{end}}>Create or update</option>
|
||||
<option value="1" {{ if eq .Action.Options.IDPConfig.Mode 1 }}selected{{end}}>Create if it doesn't exist</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row action-type action-idp">
|
||||
<label for="idIDPUser" class="col-sm-2 col-form-label">User template</label>
|
||||
<div class="col-sm-10">
|
||||
<textarea class="form-control" id="idIDPUser" name="idp_user" rows="4" placeholder=""
|
||||
aria-describedby="idpUserHelpBlock">{{.Action.Options.IDPConfig.TemplateUser}}</textarea>
|
||||
<small id="idpUserHelpBlock" class="form-text text-muted">
|
||||
Template for SFTPGo users in JSON format. Placeholders are supported
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row action-type action-idp">
|
||||
<label for="idIDAdmin" class="col-sm-2 col-form-label">Admin template</label>
|
||||
<div class="col-sm-10">
|
||||
<textarea class="form-control" id="idIDPAdmin" name="idp_admin" rows="4" placeholder=""
|
||||
aria-describedby="idpAdminHelpBlock">{{.Action.Options.IDPConfig.TemplateAdmin}}</textarea>
|
||||
<small id="idpAdminHelpBlock" class="form-text text-muted">
|
||||
Template for SFTPGo admins in JSON format. Placeholders are supported
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row action-type action-http">
|
||||
<label for="idHTTPEndpoint" class="col-sm-2 col-form-label">Endpoint</label>
|
||||
<div class="col-sm-10">
|
||||
|
@ -781,6 +813,9 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
<p>
|
||||
<span class="shortcut"><b>{{`{{RetentionReports}}`}}</b></span> => Data retention reports as zip compressed CSV files. Supported as email attachment, file path for multipart HTTP request and as single parameter for HTTP requests body.
|
||||
</p>
|
||||
<p>
|
||||
<span class="shortcut"><b>{{`{{IDPField<fieldname>}}`}}</b></span> => Identity Provider custom fields containing a string.
|
||||
</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-primary" type="button" data-dismiss="modal">OK</button>
|
||||
|
@ -1028,6 +1063,9 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
case '11':
|
||||
$('.action-pwd-expiration').show();
|
||||
break;
|
||||
case '13':
|
||||
$('.action-idp').show();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -98,6 +98,17 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row trigger trigger-idp">
|
||||
<label for="idIDPEvent" class="col-sm-2 col-form-label">IDP Login event</label>
|
||||
<div class="col-sm-10">
|
||||
<select class="form-control selectpicker" id="idIDPEvent" name="idp_login_event">
|
||||
<option value="0" {{if eq .Rule.Conditions.IDPLoginEvent 0}}selected{{end}}>Any</option>
|
||||
<option value="1" {{if eq .Rule.Conditions.IDPLoginEvent 1}}selected{{end}}>User login</option>
|
||||
<option value="2" {{if eq .Rule.Conditions.IDPLoginEvent 2}}selected{{end}}>Admin login</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card bg-light mb-3 trigger trigger-schedule">
|
||||
<div class="card-header">
|
||||
<b>Schedules</b>
|
||||
|
@ -196,7 +207,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card bg-light mb-3 trigger trigger-fs trigger-provider trigger-schedule trigger-on-demand">
|
||||
<div class="card bg-light mb-3 trigger trigger-fs trigger-provider trigger-schedule trigger-on-demand trigger-idp">
|
||||
<div class="card-header">
|
||||
<b>Name filters</b>
|
||||
</div>
|
||||
|
@ -438,7 +449,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
<b>Actions</b>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h6 class="card-title mb-4">One or more actions to execute. The "Execute sync" options is supported for upload events and required for pre-* events</h6>
|
||||
<h6 class="card-title mb-4">One or more actions to execute. The "Execute sync" options is supported for upload events and required for pre-* events and Identity provider login events if the action checks the account</h6>
|
||||
<div class="form-group row">
|
||||
<div class="col-md-12 form_field_action_outer">
|
||||
{{range $idx, $val := .Rule.Actions}}
|
||||
|
@ -727,6 +738,9 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
case '6':
|
||||
$('.trigger-on-demand').show();
|
||||
break;
|
||||
case '7':
|
||||
$('.trigger-idp').show();
|
||||
break;
|
||||
default:
|
||||
console.log(`unsupported event trigger type: ${val}`);
|
||||
}
|
||||
|
|
|
@ -10,16 +10,16 @@ require (
|
|||
require (
|
||||
github.com/fatih/color v1.15.0 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/hashicorp/go-hclog v1.4.0 // indirect
|
||||
github.com/hashicorp/go-hclog v1.5.0 // indirect
|
||||
github.com/hashicorp/yamux v0.1.1 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||
github.com/mattn/go-isatty v0.0.18 // indirect
|
||||
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
|
||||
github.com/oklog/run v1.1.0 // indirect
|
||||
golang.org/x/net v0.8.0 // indirect
|
||||
golang.org/x/sys v0.6.0 // indirect
|
||||
golang.org/x/text v0.8.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 // indirect
|
||||
google.golang.org/grpc v1.53.0 // indirect
|
||||
google.golang.org/protobuf v1.29.1 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230320184635-7606e756e683 // indirect
|
||||
google.golang.org/grpc v1.54.0 // indirect
|
||||
google.golang.org/protobuf v1.30.0 // indirect
|
||||
)
|
||||
|
|
|
@ -9,8 +9,8 @@ github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg
|
|||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/hashicorp/go-hclog v1.4.0 h1:ctuWFGrhFha8BnnzxqeRGidlEcQkDyL5u8J8t5eA11I=
|
||||
github.com/hashicorp/go-hclog v1.4.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c=
|
||||
github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-plugin v1.4.10-0.20230306173702-d78f3fc2891d h1:I5vZgh+FLXZcmwesw2ZbfW4WKiPKlZxNoxJdUNwN/wE=
|
||||
github.com/hashicorp/go-plugin v1.4.10-0.20230306173702-d78f3fc2891d/go.mod h1:6/1TEzT0eQznvI/gV2CM29DLSkAK/e58mUWKVsPaph0=
|
||||
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
|
||||
|
@ -23,8 +23,8 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
|
|||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
||||
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
|
||||
github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
|
||||
github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
|
||||
github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
|
||||
|
@ -49,14 +49,14 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
|
||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 h1:DdoeryqhaXp1LtT/emMP1BRJPHHKFi5akj/nbx/zNTA=
|
||||
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s=
|
||||
google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc=
|
||||
google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw=
|
||||
google.golang.org/genproto v0.0.0-20230320184635-7606e756e683 h1:khxVcsk/FhnzxMKOyD+TDGwjbEOpcPuIpmafPGFmhMA=
|
||||
google.golang.org/genproto v0.0.0-20230320184635-7606e756e683/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s=
|
||||
google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag=
|
||||
google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.29.1 h1:7QBf+IK2gx70Ap/hDsOmam3GE0v9HicjfEdAxE62UoM=
|
||||
google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
|
@ -10,16 +10,16 @@ require (
|
|||
require (
|
||||
github.com/fatih/color v1.15.0 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/hashicorp/go-hclog v1.4.0 // indirect
|
||||
github.com/hashicorp/go-hclog v1.5.0 // indirect
|
||||
github.com/hashicorp/yamux v0.1.1 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||
github.com/mattn/go-isatty v0.0.18 // indirect
|
||||
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
|
||||
github.com/oklog/run v1.1.0 // indirect
|
||||
golang.org/x/net v0.8.0 // indirect
|
||||
golang.org/x/sys v0.6.0 // indirect
|
||||
golang.org/x/text v0.8.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 // indirect
|
||||
google.golang.org/grpc v1.53.0 // indirect
|
||||
google.golang.org/protobuf v1.29.1 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230320184635-7606e756e683 // indirect
|
||||
google.golang.org/grpc v1.54.0 // indirect
|
||||
google.golang.org/protobuf v1.30.0 // indirect
|
||||
)
|
||||
|
|
|
@ -9,8 +9,8 @@ github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg
|
|||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/hashicorp/go-hclog v1.4.0 h1:ctuWFGrhFha8BnnzxqeRGidlEcQkDyL5u8J8t5eA11I=
|
||||
github.com/hashicorp/go-hclog v1.4.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c=
|
||||
github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-plugin v1.4.10-0.20230306173702-d78f3fc2891d h1:I5vZgh+FLXZcmwesw2ZbfW4WKiPKlZxNoxJdUNwN/wE=
|
||||
github.com/hashicorp/go-plugin v1.4.10-0.20230306173702-d78f3fc2891d/go.mod h1:6/1TEzT0eQznvI/gV2CM29DLSkAK/e58mUWKVsPaph0=
|
||||
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
|
||||
|
@ -23,8 +23,8 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
|
|||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
||||
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
|
||||
github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
|
||||
github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
|
||||
github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
|
||||
|
@ -49,14 +49,14 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
|
||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 h1:DdoeryqhaXp1LtT/emMP1BRJPHHKFi5akj/nbx/zNTA=
|
||||
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s=
|
||||
google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc=
|
||||
google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw=
|
||||
google.golang.org/genproto v0.0.0-20230320184635-7606e756e683 h1:khxVcsk/FhnzxMKOyD+TDGwjbEOpcPuIpmafPGFmhMA=
|
||||
google.golang.org/genproto v0.0.0-20230320184635-7606e756e683/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s=
|
||||
google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag=
|
||||
google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.29.1 h1:7QBf+IK2gx70Ap/hDsOmam3GE0v9HicjfEdAxE62UoM=
|
||||
google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
Loading…
Reference in a new issue