EventManager: add IDP login trigger and check account action

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
Nicola Murino 2023-03-22 19:02:54 +01:00
parent 40344ec0ff
commit e29f6857db
No known key found for this signature in database
GPG key ID: 935D2952DEC4EECF
58 changed files with 1660 additions and 385 deletions

View file

@ -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. - `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. - `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. - `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: - `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. - `Rename`. You can rename one or more files or directories.
- `Delete`. You can delete one or more files and 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. - `{{Timestamp}}`. Event timestamp as nanoseconds since epoch.
- `{{ObjectData}}`. Provider object data serialized as JSON with sensitive fields removed. - `{{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. - `{{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. 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: 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). - `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. - `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. - `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 rules actions are taken. For example you can react to uploads only if they are performed by a particular user or using a specified protocol. You can further restrict a rule by specifying additional conditions that must be met before the rules actions are taken. For example you can react to uploads only if they are performed by a particular user or using a specified protocol.

View file

@ -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). 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). 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. 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, 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
View file

@ -3,22 +3,22 @@ module github.com/drakkan/sftpgo/v2
go 1.20 go 1.20
require ( 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/azcore v1.4.0
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.0.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/alexedwards/argon2id v0.0.0-20230305115115-4b3c3280a736
github.com/aws/aws-sdk-go-v2 v1.17.6 github.com/aws/aws-sdk-go-v2 v1.17.7
github.com/aws/aws-sdk-go-v2/config v1.18.17 github.com/aws/aws-sdk-go-v2/config v1.18.19
github.com/aws/aws-sdk-go-v2/credentials v1.13.17 github.com/aws/aws-sdk-go-v2/credentials v1.13.18
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.0 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.57 github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.59
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.14.6 github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.14.7
github.com/aws/aws-sdk-go-v2/service/s3 v1.30.6 github.com/aws/aws-sdk-go-v2/service/s3 v1.31.0
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.0 github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.1
github.com/aws/aws-sdk-go-v2/service/sts v1.18.6 github.com/aws/aws-sdk-go-v2/service/sts v1.18.7
github.com/bmatcuk/doublestar/v4 v4.6.0 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/coreos/go-oidc/v3 v3.5.0
github.com/drakkan/webdav v0.0.0-20230227175313-32996838bcd8 github.com/drakkan/webdav v0.0.0-20230227175313-32996838bcd8
github.com/eikenb/pipeat v0.0.0-20210730190139-06b3e6902001 github.com/eikenb/pipeat v0.0.0-20210730190139-06b3e6902001
@ -32,13 +32,13 @@ require (
github.com/golang/mock v1.6.0 github.com/golang/mock v1.6.0
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/google/uuid v1.3.0 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-plugin v1.4.10-0.20230306173702-d78f3fc2891d
github.com/hashicorp/go-retryablehttp v0.7.2 github.com/hashicorp/go-retryablehttp v0.7.2
github.com/jackc/pgx/v5 v5.3.2-0.20230311213408-9ae852eb583d github.com/jackc/pgx/v5 v5.3.2-0.20230311213408-9ae852eb583d
github.com/jlaffaye/ftp v0.0.0-20201112195030-9aae4d151126 github.com/jlaffaye/ftp v0.0.0-20201112195030-9aae4d151126
github.com/klauspost/compress v1.16.3 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/lithammer/shortuuid/v3 v3.0.7
github.com/mattn/go-sqlite3 v1.14.16 github.com/mattn/go-sqlite3 v1.14.16
github.com/mhale/smtpd v0.8.0 github.com/mhale/smtpd v0.8.0
@ -62,10 +62,10 @@ require (
github.com/subosito/gotenv v1.4.2 github.com/subosito/gotenv v1.4.2
github.com/unrolled/secure v1.13.0 github.com/unrolled/secure v1.13.0
github.com/wagslane/go-password-validator v0.3.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 github.com/yl2chen/cidranger v1.0.3-0.20210928021809-d1cb2c52f37a
go.etcd.io/bbolt v1.3.7 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 gocloud.dev v0.29.0
golang.org/x/crypto v0.7.0 golang.org/x/crypto v0.7.0
golang.org/x/net v0.8.0 golang.org/x/net v0.8.0
@ -73,7 +73,7 @@ require (
golang.org/x/sys v0.6.0 golang.org/x/sys v0.6.0
golang.org/x/term v0.6.0 golang.org/x/term v0.6.0
golang.org/x/time v0.3.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 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 v0.110.0 // indirect
cloud.google.com/go/compute v1.18.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/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/Azure/azure-sdk-for-go/sdk/internal v1.2.0 // indirect
github.com/ajg/form v1.5.1 // 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/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/configsources v1.1.31 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.24 // 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.31 // 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.22 // 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/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/checksum v1.1.26 // 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/presigned-url v1.9.25 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.24 // 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.5 // 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.5 // 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/aws/smithy-go v1.13.5 // indirect
github.com/beorn7/perks v1.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect
github.com/boombuler/barcode 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/fsnotify/fsnotify v1.6.0 // indirect
github.com/go-jose/go-jose/v3 v3.0.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/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/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect github.com/golang/protobuf v1.5.3 // indirect
github.com/google/go-cmp v0.5.9 // 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/lufia/plan9stats v0.0.0-20230110061619-bbe2e5e100de // indirect
github.com/magiconair/properties v1.8.7 // indirect github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-colorable v0.1.13 // 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/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/miekg/dns v1.1.52 // indirect github.com/miekg/dns v1.1.52 // indirect
github.com/minio/sha256-simd v1.0.0 // 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/tools v0.7.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/appengine v1.6.7 // indirect google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 // indirect google.golang.org/genproto v0.0.0-20230320184635-7606e756e683 // indirect
google.golang.org/grpc v1.53.0 // indirect google.golang.org/grpc v1.54.0 // indirect
google.golang.org/protobuf v1.29.1 // indirect google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
) )

126
go.sum
View file

@ -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.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.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.10.0/go.mod h1:nXAECrMt2qHpF6RZUZseteD6QyanL68reN4OXPw0UWM=
cloud.google.com/go/iam v0.12.0 h1:DRtTY29b75ciH6Ov1PHb4/iat2CLCvrOm40Q0a6DFpE= cloud.google.com/go/iam v0.13.0 h1:+CmB+K0J/33d0zSQ9SlFWUeCCEn5XJA0ZMZ3pHE9u8k=
cloud.google.com/go/iam v0.12.0/go.mod h1:knyHGviacl11zrtZUoDuYpDgLjvr28sLQaG0YB2GYAY= 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.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/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= 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.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.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.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.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.5.0/go.mod h1:dxNzUopWy7RQevYFHewchb29POFv3/AaBgnhqzqiK0w=
cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I= 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= 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/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/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/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-20230320061759-8cc1b52080c5 h1:IEjq88XO4PuBDcvmjQJcQGg+w+UaafSy8G5Kcb5tBhI=
github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962/go.mod h1:kC29dT1vFpj7py2OvG1khBdQpo3kInWP+6QipLbdngo= 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/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.0/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
github.com/HdrHistogram/hdrhistogram-go v1.1.2/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 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.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.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.7 h1:CLSjnhJSTSogvqUGhIC6LqFKATMRexcxLZ0i/Nzk9Eg=
github.com/aws/aws-sdk-go-v2 v1.17.6/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= 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 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/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.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.19 h1:AqFK6zFNtq4i1EYu+eC7lcKHYnZagMn6SW171la0bGw=
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/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.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.18 h1:EQMdtHwz0ILTW1hoP+EwuWhwCG1hD6l3+RWFQABET4c=
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/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.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.1 h1:gt57MN3liKiyGopcqgNzJb2+d9MJaKT/q1OksHNXVE4=
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/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.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.59 h1:E3Y+OfzOK1+rmRo/K2G0ml8Vs+Xqk0kOnf4nS0kUtBc=
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/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.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.31 h1:sJLYcS+eZn5EeNINGHSCRAwUJMFVqklwkH36Vbyai7M=
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/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.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.25 h1:1mnRASEKnkqsntcxHaysxwgVoUUp5dkiB+l3llKnqyg=
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/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.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.32 h1:p5luUImdIqywn6JpQsW3tq5GNOxKmOnEpybzPx+d1lk=
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/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.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.23 h1:DWYZIsyqagnWL00f8M/SOr9fN063OEQWn9LLTbdYXsk=
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/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/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 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/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.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.26 h1:CeuSeq/8FnYpPtnuIeLQEEvDv9zUjneuYi8EghMBdwQ=
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/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.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.25 h1:5LHn8JQ0qvjD9L9JhMtylnkcw7j05GDZqM9Oin6hpr0=
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/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.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.14.0 h1:e2ooMhpYGhDnBfSvIyusvAwX7KexuZaHbQY2Dyei7VU=
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/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/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.7 h1:QA+w3BlShNMgOTRN1kBG2qOIHuTzVTxZ0l3ImKkz+ic=
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/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.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.31.0 h1:B1G2pSPvbAtQjilPq+Y7jLIzCOwKzuVEl+aBBaNG0AQ=
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/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.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.1 h1:+rANS0SbrDUqF3VJeil1HJHhNK8vdUu1VGqnkr4o6kw=
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/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/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/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/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.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.6 h1:5V7DWLBd7wTELVz5bPpwzYy/sikk0gsgZfj40X+l5OI=
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/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.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.6 h1:B8cauxOH1W1v7rd8RdI/MWnoR4Ze0wIHWrb90qczxj4=
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/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.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.7 h1:bWNgNdRko2x6gqa0blfATqAZKZokPIeM1vfmQt2pnvM=
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/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.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 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8=
github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= 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-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/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/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.3 h1:fNmtG6XhoA1DhdDCIu66YyGSsNb1szj4CaAsbDxRmy4=
github.com/cockroachdb/cockroach-go/v2 v2.3.2/go.mod h1:1wNJ45eSXW9AnOc3skntW9ZUZz6gxrQK3cOj3rK+BC8= 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-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= 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= 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/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/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= 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.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.1 h1:lEs5Ob+oOG/Ze199njvzHbhn6p9T+h64F5hRj69iTTo= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/goccy/go-json v0.10.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/goccy/go-yaml v1.9.5/go.mod h1:U/jl18uSupI5rdI2jmuCswEA2htH9eXfferR3KfscvA= 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-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/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.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 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.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-hclog v1.4.0 h1:ctuWFGrhFha8BnnzxqeRGidlEcQkDyL5u8J8t5eA11I= github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c=
github.com/hashicorp/go-hclog v1.4.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= 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.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.2.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-immutable-radix v1.3.1/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/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 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI=
github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4= 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.9 h1:TRX4Q630UXxPVLvP5vGaqVJO7S+0PE6msRZUsFSBoC8=
github.com/lestrrat-go/jwx/v2 v2.0.8/go.mod h1:zLxnyv9rTlEvOUHbc48FAfIL8iYu2hHvIRaTFGc8mT0= 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.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 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU=
github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= 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.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.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.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.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 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-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.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
github.com/mattn/go-shellwords v1.0.6/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/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-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= 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.9 h1:Q4DbCk3htT5DtDWKeMgNXCiHc4bBY/vv/XQPT6XDXzc=
github.com/wneessen/go-mail v0.3.8/go.mod h1:m25lkU2GYQnlVr6tdwK533/UXxo57V0kLOjaFYmub0E= 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/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.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= 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.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.9.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/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.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.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-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/goleak v1.1.11/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.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.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY=
google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI= 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.114.0 h1:1xQPji6cO2E2vLiI+C/XiFAnsn1WV3mjaEwGLhi3grE=
google.golang.org/api v0.112.0/go.mod h1:737UfWHNsOq4F3REUTmb+GN9pugkgNLCayLTfoIKpPc= 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.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.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.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-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-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-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-20230320184635-7606e756e683 h1:khxVcsk/FhnzxMKOyD+TDGwjbEOpcPuIpmafPGFmhMA=
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= 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 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.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 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.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.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.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.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/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-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 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.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.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.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.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 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/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/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= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View file

@ -287,10 +287,7 @@ func (c *Configuration) validateChallenges() error {
if err := c.HTTP01Challenge.validate(); err != nil { if err := c.HTTP01Challenge.validate(); err != nil {
return err return err
} }
if err := c.TLSALPN01Challenge.validate(); err != nil { return c.TLSALPN01Challenge.validate()
return err
}
return nil
} }
func (c *Configuration) checkDomains() { func (c *Configuration) checkDomains() {

View file

@ -321,7 +321,7 @@ type actionHandlerStub struct {
called bool called bool
} }
func (h *actionHandlerStub) Handle(event *notifier.FsEvent) (int, error) { func (h *actionHandlerStub) Handle(_ *notifier.FsEvent) (int, error) {
h.called = true h.called = true
return 1, nil return 1, nil

View file

@ -1235,7 +1235,7 @@ func TestParseAllowedIPAndRanges(t *testing.T) {
assert.False(t, allow[1](net.ParseIP("172.16.1.1"))) 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, for _, provider := range []sdk.FilesystemProvider{sdk.LocalFilesystemProvider,
sdk.CryptedFilesystemProvider, sdk.S3FilesystemProvider, sdk.GCSFilesystemProvider, sdk.CryptedFilesystemProvider, sdk.S3FilesystemProvider, sdk.GCSFilesystemProvider,
sdk.AzureBlobFilesystemProvider, sdk.SFTPFilesystemProvider, sdk.AzureBlobFilesystemProvider, sdk.SFTPFilesystemProvider,

View file

@ -63,7 +63,7 @@ func (fs *MockOsFs) IsUploadResumeSupported() bool {
return !fs.hasVirtualFolders 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 return vfs.ErrVfsUnsupported
} }
@ -75,7 +75,7 @@ func (fs *MockOsFs) Lstat(name string) (os.FileInfo, error) {
} }
// Walk returns a duplicate path for testing // 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 { if fs.err == errWalkDir {
walkFn("fsdpath", vfs.NewFileInfo("dpath", true, 0, time.Now(), false), nil) //nolint:errcheck 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 return walkFn("fsdpath", vfs.NewFileInfo("dpath", true, 0, time.Now(), false), nil) //nolint:errcheck

View file

@ -18,6 +18,7 @@ import (
"bytes" "bytes"
"context" "context"
"encoding/csv" "encoding/csv"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"io" "io"
@ -56,6 +57,12 @@ const (
maxAttachmentsSize = int64(10 * 1024 * 1024) maxAttachmentsSize = int64(10 * 1024 * 1024)
) )
// Supported IDP login events
const (
IDPLoginUser = "IDP login user"
IDPLoginAdmin = "IDP login admin"
)
var ( var (
// eventManager handle the supported event rules actions // eventManager handle the supported event rules actions
eventManager eventRulesContainer eventManager eventRulesContainer
@ -90,6 +97,11 @@ func HandleCertificateEvent(params EventParams) {
eventManager.handleCertificateEvent(params) 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 // eventRulesContainer stores event rules by trigger
type eventRulesContainer struct { type eventRulesContainer struct {
sync.RWMutex sync.RWMutex
@ -99,6 +111,7 @@ type eventRulesContainer struct {
Schedules []dataprovider.EventRule Schedules []dataprovider.EventRule
IPBlockedEvents []dataprovider.EventRule IPBlockedEvents []dataprovider.EventRule
CertificateEvents []dataprovider.EventRule CertificateEvents []dataprovider.EventRule
IPDLoginEvents []dataprovider.EventRule
schedulesMapping map[string][]cron.EntryID schedulesMapping map[string][]cron.EntryID
concurrencyGuard chan struct{} concurrencyGuard chan struct{}
} }
@ -168,6 +181,15 @@ func (r *eventRulesContainer) removeRuleInternal(name string) {
return 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 { for idx := range r.Schedules {
if r.Schedules[idx].Name == name { if r.Schedules[idx].Name == name {
if schedules, ok := r.schedulesMapping[name]; ok { if schedules, ok := r.schedulesMapping[name]; ok {
@ -213,6 +235,9 @@ func (r *eventRulesContainer) addUpdateRuleInternal(rule dataprovider.EventRule)
case dataprovider.EventTriggerCertificate: case dataprovider.EventTriggerCertificate:
r.CertificateEvents = append(r.CertificateEvents, rule) r.CertificateEvents = append(r.CertificateEvents, rule)
eventManagerLog(logger.LevelDebug, "added rule %q to certificate events", rule.Name) 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: case dataprovider.EventTriggerSchedule:
for _, schedule := range rule.Conditions.Schedules { for _, schedule := range rule.Conditions.Schedules {
cronSpec := schedule.GetCronSpec() cronSpec := schedule.GetCronSpec()
@ -253,13 +278,27 @@ func (r *eventRulesContainer) loadRules() {
r.addUpdateRuleInternal(rule) r.addUpdateRuleInternal(rule)
} }
} }
eventManagerLog(logger.LevelDebug, "event rules updated, fs events: %d, provider events: %d, schedules: %d, ip blocked events: %d, certificate events: %d", 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.FsEvents), len(r.ProviderEvents), len(r.Schedules), len(r.IPBlockedEvents), len(r.CertificateEvents), len(r.IPDLoginEvents))
r.setLastLoadTime(modTime) 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) { if !util.Contains(conditions.ProviderEvents, params.Event) {
return false return false
} }
@ -275,7 +314,7 @@ func (r *eventRulesContainer) checkProviderEventMatch(conditions dataprovider.Ev
return true 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) { if !util.Contains(conditions.FsEvents, params.Event) {
return false return false
} }
@ -327,7 +366,7 @@ func (r *eventRulesContainer) handleFsEvent(params EventParams) (bool, error) {
var rulesWithSyncActions, rulesAsync []dataprovider.EventRule var rulesWithSyncActions, rulesAsync []dataprovider.EventRule
for _, rule := range r.FsEvents { for _, rule := range r.FsEvents {
if r.checkFsEventMatch(rule.Conditions, params) { if r.checkFsEventMatch(&rule.Conditions, &params) {
if err := rule.CheckActionsConsistency(""); err != nil { if err := rule.CheckActionsConsistency(""); err != nil {
eventManagerLog(logger.LevelWarn, "rule %q skipped: %v, event %q", eventManagerLog(logger.LevelWarn, "rule %q skipped: %v, event %q",
rule.Name, err, params.Event) rule.Name, err, params.Event)
@ -361,6 +400,59 @@ func (r *eventRulesContainer) handleFsEvent(params EventParams) (bool, error) {
return false, nil 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, &params) {
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 // username is populated for user objects
func (r *eventRulesContainer) handleProviderEvent(params EventParams) { func (r *eventRulesContainer) handleProviderEvent(params EventParams) {
r.RLock() r.RLock()
@ -368,7 +460,7 @@ func (r *eventRulesContainer) handleProviderEvent(params EventParams) {
var rules []dataprovider.EventRule var rules []dataprovider.EventRule
for _, rule := range r.ProviderEvents { for _, rule := range r.ProviderEvents {
if r.checkProviderEventMatch(rule.Conditions, params) { if r.checkProviderEventMatch(&rule.Conditions, &params) {
if err := rule.CheckActionsConsistency(params.ObjectType); err == nil { if err := rule.CheckActionsConsistency(params.ObjectType); err == nil {
rules = append(rules, rule) rules = append(rules, rule)
} else { } else {
@ -452,6 +544,7 @@ type EventParams struct {
IP string IP string
Role string Role string
Timestamp int64 Timestamp int64
IDPCustomFields *map[string]string
Object plugin.Renderer Object plugin.Renderer
sender string sender string
updateStatusFromError bool updateStatusFromError bool
@ -474,10 +567,32 @@ func (p *EventParams) getACopy() *EventParams {
retentionChecks = append(retentionChecks, executedCheck) retentionChecks = append(retentionChecks, executedCheck)
} }
params.retentionChecks = retentionChecks 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 &params return &params
} }
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 // AddError adds a new error to the event params and update the status if needed
func (p *EventParams) AddError(err error) { func (p *EventParams) AddError(err error) {
if err == nil { if err == nil {
@ -622,34 +737,41 @@ func (p *EventParams) getRetentionReportsAsMailAttachment() (*mail.File, error)
}, nil }, 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{ replacements := []string{
"{{Name}}", p.Name, "{{Name}}", p.getStringReplacement(p.Name, jsonEscaped),
"{{Event}}", p.Event, "{{Event}}", p.Event,
"{{Status}}", fmt.Sprintf("%d", p.Status), "{{Status}}", fmt.Sprintf("%d", p.Status),
"{{VirtualPath}}", p.VirtualPath, "{{VirtualPath}}", p.getStringReplacement(p.VirtualPath, jsonEscaped),
"{{FsPath}}", p.FsPath, "{{FsPath}}", p.getStringReplacement(p.FsPath, jsonEscaped),
"{{VirtualTargetPath}}", p.VirtualTargetPath, "{{VirtualTargetPath}}", p.getStringReplacement(p.VirtualTargetPath, jsonEscaped),
"{{FsTargetPath}}", p.FsTargetPath, "{{FsTargetPath}}", p.getStringReplacement(p.FsTargetPath, jsonEscaped),
"{{ObjectName}}", p.ObjectName, "{{ObjectName}}", p.getStringReplacement(p.ObjectName, jsonEscaped),
"{{ObjectType}}", p.ObjectType, "{{ObjectType}}", p.ObjectType,
"{{FileSize}}", fmt.Sprintf("%d", p.FileSize), "{{FileSize}}", fmt.Sprintf("%d", p.FileSize),
"{{Elapsed}}", fmt.Sprintf("%d", p.Elapsed), "{{Elapsed}}", fmt.Sprintf("%d", p.Elapsed),
"{{Protocol}}", p.Protocol, "{{Protocol}}", p.Protocol,
"{{IP}}", p.IP, "{{IP}}", p.IP,
"{{Role}}", p.Role, "{{Role}}", p.getStringReplacement(p.Role, jsonEscaped),
"{{Timestamp}}", fmt.Sprintf("%d", p.Timestamp), "{{Timestamp}}", fmt.Sprintf("%d", p.Timestamp),
"{{StatusString}}", p.getStatusString(), "{{StatusString}}", p.getStatusString(),
} }
if p.VirtualPath != "" { 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 != "" { if p.VirtualTargetPath != "" {
replacements = append(replacements, "{{VirtualTargetDirPath}}", path.Dir(p.VirtualTargetPath)) replacements = append(replacements, "{{VirtualTargetDirPath}}", p.getStringReplacement(path.Dir(p.VirtualTargetPath), jsonEscaped))
replacements = append(replacements, "{{TargetName}}", path.Base(p.VirtualTargetPath)) replacements = append(replacements, "{{TargetName}}", p.getStringReplacement(path.Base(p.VirtualTargetPath), jsonEscaped))
} }
if len(p.errors) > 0 { 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 { } else {
replacements = append(replacements, "{{ErrorString}}", "") replacements = append(replacements, "{{ErrorString}}", "")
} }
@ -660,6 +782,11 @@ func (p *EventParams) getStringReplacements(addObjectData bool) []string {
replacements[len(replacements)-1] = string(data) 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 return replacements
} }
@ -1060,7 +1187,7 @@ func getHTTPRuleActionEndpoint(c dataprovider.EventActionHTTPConfig, replacer *s
} }
func writeHTTPPart(m *multipart.Writer, part dataprovider.HTTPPart, h textproto.MIMEHeader, 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 { ) error {
partWriter, err := m.CreatePart(h) partWriter, err := m.CreatePart(h)
if err != nil { if err != nil {
@ -1068,7 +1195,14 @@ func writeHTTPPart(m *multipart.Writer, part dataprovider.HTTPPart, h textproto.
return err return err
} }
if part.Body != "" { 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 { if err != nil {
eventManagerLog(logger.LevelError, "unable to write part %q, err: %v", part.Name, err) eventManagerLog(logger.LevelError, "unable to write part %q, err: %v", part.Name, err)
return err return err
@ -1095,8 +1229,8 @@ func writeHTTPPart(m *multipart.Writer, part dataprovider.HTTPPart, h textproto.
return nil return nil
} }
func getHTTPRuleActionBody(c dataprovider.EventActionHTTPConfig, replacer *strings.Replacer, func getHTTPRuleActionBody(c *dataprovider.EventActionHTTPConfig, replacer *strings.Replacer,
cancel context.CancelFunc, user dataprovider.User, params *EventParams, cancel context.CancelFunc, user dataprovider.User, params *EventParams, addObjectData bool,
) (io.ReadCloser, string, error) { ) (io.ReadCloser, string, error) {
var body io.ReadCloser var body io.ReadCloser
if c.Method == http.MethodGet { if c.Method == http.MethodGet {
@ -1110,6 +1244,11 @@ func getHTTPRuleActionBody(c dataprovider.EventActionHTTPConfig, replacer *strin
} }
return io.NopCloser(bytes.NewBuffer(data)), "", nil 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 return io.NopCloser(bytes.NewBufferString(replaceWithReplacer(c.Body, replacer))), "", nil
} }
if len(c.Parts) > 0 { if len(c.Parts) > 0 {
@ -1154,7 +1293,7 @@ func getHTTPRuleActionBody(c dataprovider.EventActionHTTPConfig, replacer *strin
for _, keyVal := range part.Headers { for _, keyVal := range part.Headers {
h.Set(keyVal.Key, replaceWithReplacer(keyVal.Value, replacer)) 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() cancel()
return return
} }
@ -1176,7 +1315,7 @@ func executeHTTPRuleAction(c dataprovider.EventActionHTTPConfig, params *EventPa
addObjectData = c.HasObjectData() addObjectData = c.HasObjectData()
} }
replacements := params.getStringReplacements(addObjectData) replacements := params.getStringReplacements(addObjectData, false)
replacer := strings.NewReplacer(replacements...) replacer := strings.NewReplacer(replacements...)
endpoint, err := getHTTPRuleActionEndpoint(c, replacer) endpoint, err := getHTTPRuleActionEndpoint(c, replacer)
if err != nil { if err != nil {
@ -1193,7 +1332,7 @@ func executeHTTPRuleAction(c dataprovider.EventActionHTTPConfig, params *EventPa
return err return err
} }
} }
body, contentType, err := getHTTPRuleActionBody(c, replacer, cancel, user, params) body, contentType, err := getHTTPRuleActionBody(&c, replacer, cancel, user, params, addObjectData)
if err != nil { if err != nil {
return err 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...) replacer := strings.NewReplacer(replacements...)
args := make([]string, 0, len(c.Args)) args := make([]string, 0, len(c.Args))
@ -1277,7 +1416,7 @@ func executeEmailRuleAction(c dataprovider.EventActionEmailConfig, params *Event
addObjectData = true addObjectData = true
} }
} }
replacements := params.getStringReplacements(addObjectData) replacements := params.getStringReplacements(addObjectData, false)
replacer := strings.NewReplacer(replacements...) replacer := strings.NewReplacer(replacements...)
body := replaceWithReplacer(c.Body, replacer) body := replaceWithReplacer(c.Body, replacer)
subject := replaceWithReplacer(c.Subject, replacer) subject := replaceWithReplacer(c.Subject, replacer)
@ -1825,7 +1964,7 @@ func executeFsRuleAction(c dataprovider.EventActionFilesystemConfig, conditions
params *EventParams, params *EventParams,
) error { ) error {
addObjectData := false addObjectData := false
replacements := params.getStringReplacements(addObjectData) replacements := params.getStringReplacements(addObjectData, false)
replacer := strings.NewReplacer(replacements...) replacer := strings.NewReplacer(replacements...)
switch c.Type { switch c.Type {
case dataprovider.FilesystemActionRename: case dataprovider.FilesystemActionRename:
@ -2207,6 +2346,71 @@ func executePwdExpirationCheckRuleAction(config dataprovider.EventActionPassword
return nil 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, func executeRuleAction(action dataprovider.BaseEventAction, params *EventParams,
conditions dataprovider.ConditionOptions, conditions dataprovider.ConditionOptions,
) error { ) error {
@ -2252,6 +2456,43 @@ func executeRuleAction(action dataprovider.BaseEventAction, params *EventParams,
return err 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 { func executeSyncRulesActions(rules []dataprovider.EventRule, params EventParams) error {
var errRes error var errRes error

View file

@ -46,7 +46,7 @@ import (
func TestEventRuleMatch(t *testing.T) { func TestEventRuleMatch(t *testing.T) {
role := "role1" role := "role1"
conditions := dataprovider.EventConditions{ conditions := &dataprovider.EventConditions{
ProviderEvents: []string{"add", "update"}, ProviderEvents: []string{"add", "update"},
Options: dataprovider.ConditionOptions{ Options: dataprovider.ConditionOptions{
Names: []dataprovider.ConditionPattern{ Names: []dataprovider.ConditionPattern{
@ -62,40 +62,40 @@ func TestEventRuleMatch(t *testing.T) {
}, },
}, },
} }
res := eventManager.checkProviderEventMatch(conditions, EventParams{ res := eventManager.checkProviderEventMatch(conditions, &EventParams{
Name: "user1", Name: "user1",
Role: role, Role: role,
Event: "add", Event: "add",
}) })
assert.False(t, res) assert.False(t, res)
res = eventManager.checkProviderEventMatch(conditions, EventParams{ res = eventManager.checkProviderEventMatch(conditions, &EventParams{
Name: "user2", Name: "user2",
Role: role, Role: role,
Event: "update", Event: "update",
}) })
assert.True(t, res) assert.True(t, res)
res = eventManager.checkProviderEventMatch(conditions, EventParams{ res = eventManager.checkProviderEventMatch(conditions, &EventParams{
Name: "user2", Name: "user2",
Role: role, Role: role,
Event: "delete", Event: "delete",
}) })
assert.False(t, res) assert.False(t, res)
conditions.Options.ProviderObjects = []string{"api_key"} conditions.Options.ProviderObjects = []string{"api_key"}
res = eventManager.checkProviderEventMatch(conditions, EventParams{ res = eventManager.checkProviderEventMatch(conditions, &EventParams{
Name: "user2", Name: "user2",
Event: "update", Event: "update",
Role: role, Role: role,
ObjectType: "share", ObjectType: "share",
}) })
assert.False(t, res) assert.False(t, res)
res = eventManager.checkProviderEventMatch(conditions, EventParams{ res = eventManager.checkProviderEventMatch(conditions, &EventParams{
Name: "user2", Name: "user2",
Event: "update", Event: "update",
Role: role, Role: role,
ObjectType: "api_key", ObjectType: "api_key",
}) })
assert.True(t, res) assert.True(t, res)
res = eventManager.checkProviderEventMatch(conditions, EventParams{ res = eventManager.checkProviderEventMatch(conditions, &EventParams{
Name: "user2", Name: "user2",
Event: "update", Event: "update",
Role: role + "1", Role: role + "1",
@ -103,7 +103,7 @@ func TestEventRuleMatch(t *testing.T) {
}) })
assert.False(t, res) assert.False(t, res)
// now test fs events // now test fs events
conditions = dataprovider.EventConditions{ conditions = &dataprovider.EventConditions{
FsEvents: []string{operationUpload, operationDownload}, FsEvents: []string{operationUpload, operationDownload},
Options: dataprovider.ConditionOptions{ Options: dataprovider.ConditionOptions{
Names: []dataprovider.ConditionPattern{ Names: []dataprovider.ConditionPattern{
@ -138,41 +138,41 @@ func TestEventRuleMatch(t *testing.T) {
ObjectName: "path.txt", ObjectName: "path.txt",
FileSize: 20, FileSize: 20,
} }
res = eventManager.checkFsEventMatch(conditions, params) res = eventManager.checkFsEventMatch(conditions, &params)
assert.False(t, res) assert.False(t, res)
params.Event = operationDownload params.Event = operationDownload
res = eventManager.checkFsEventMatch(conditions, params) res = eventManager.checkFsEventMatch(conditions, &params)
assert.True(t, res) assert.True(t, res)
params.Role = role params.Role = role
res = eventManager.checkFsEventMatch(conditions, params) res = eventManager.checkFsEventMatch(conditions, &params)
assert.False(t, res) assert.False(t, res)
params.Role = "" params.Role = ""
params.Name = "name" params.Name = "name"
res = eventManager.checkFsEventMatch(conditions, params) res = eventManager.checkFsEventMatch(conditions, &params)
assert.False(t, res) assert.False(t, res)
params.Name = "user5" params.Name = "user5"
res = eventManager.checkFsEventMatch(conditions, params) res = eventManager.checkFsEventMatch(conditions, &params)
assert.True(t, res) assert.True(t, res)
params.VirtualPath = "/sub/f.jpg" params.VirtualPath = "/sub/f.jpg"
params.ObjectName = path.Base(params.VirtualPath) params.ObjectName = path.Base(params.VirtualPath)
res = eventManager.checkFsEventMatch(conditions, params) res = eventManager.checkFsEventMatch(conditions, &params)
assert.False(t, res) assert.False(t, res)
params.VirtualPath = "/sub/f.txt" params.VirtualPath = "/sub/f.txt"
params.ObjectName = path.Base(params.VirtualPath) params.ObjectName = path.Base(params.VirtualPath)
res = eventManager.checkFsEventMatch(conditions, params) res = eventManager.checkFsEventMatch(conditions, &params)
assert.True(t, res) assert.True(t, res)
params.Protocol = ProtocolHTTP params.Protocol = ProtocolHTTP
res = eventManager.checkFsEventMatch(conditions, params) res = eventManager.checkFsEventMatch(conditions, &params)
assert.False(t, res) assert.False(t, res)
params.Protocol = ProtocolSFTP params.Protocol = ProtocolSFTP
params.FileSize = 5 params.FileSize = 5
res = eventManager.checkFsEventMatch(conditions, params) res = eventManager.checkFsEventMatch(conditions, &params)
assert.False(t, res) assert.False(t, res)
params.FileSize = 50 params.FileSize = 50
res = eventManager.checkFsEventMatch(conditions, params) res = eventManager.checkFsEventMatch(conditions, &params)
assert.False(t, res) assert.False(t, res)
params.FileSize = 25 params.FileSize = 25
res = eventManager.checkFsEventMatch(conditions, params) res = eventManager.checkFsEventMatch(conditions, &params)
assert.True(t, res) assert.True(t, res)
// bad pattern // bad pattern
conditions.Options.Names = []dataprovider.ConditionPattern{ conditions.Options.Names = []dataprovider.ConditionPattern{
@ -180,10 +180,10 @@ func TestEventRuleMatch(t *testing.T) {
Pattern: "[-]", Pattern: "[-]",
}, },
} }
res = eventManager.checkFsEventMatch(conditions, params) res = eventManager.checkFsEventMatch(conditions, &params)
assert.False(t, res) assert.False(t, res)
// check fs events with group name filters // check fs events with group name filters
conditions = dataprovider.EventConditions{ conditions = &dataprovider.EventConditions{
FsEvents: []string{operationUpload, operationDownload}, FsEvents: []string{operationUpload, operationDownload},
Options: dataprovider.ConditionOptions{ Options: dataprovider.ConditionOptions{
GroupNames: []dataprovider.ConditionPattern{ GroupNames: []dataprovider.ConditionPattern{
@ -200,7 +200,7 @@ func TestEventRuleMatch(t *testing.T) {
Name: "user1", Name: "user1",
Event: operationUpload, Event: operationUpload,
} }
res = eventManager.checkFsEventMatch(conditions, params) res = eventManager.checkFsEventMatch(conditions, &params)
assert.False(t, res) assert.False(t, res)
params.Groups = []sdk.GroupMapping{ params.Groups = []sdk.GroupMapping{
{ {
@ -212,7 +212,7 @@ func TestEventRuleMatch(t *testing.T) {
Type: sdk.GroupTypeSecondary, Type: sdk.GroupTypeSecondary,
}, },
} }
res = eventManager.checkFsEventMatch(conditions, params) res = eventManager.checkFsEventMatch(conditions, &params)
assert.False(t, res) assert.False(t, res)
params.Groups = []sdk.GroupMapping{ params.Groups = []sdk.GroupMapping{
{ {
@ -224,7 +224,7 @@ func TestEventRuleMatch(t *testing.T) {
Type: sdk.GroupTypeSecondary, Type: sdk.GroupTypeSecondary,
}, },
} }
res = eventManager.checkFsEventMatch(conditions, params) res = eventManager.checkFsEventMatch(conditions, &params)
assert.True(t, res) assert.True(t, res)
// check user conditions // check user conditions
user := dataprovider.User{} user := dataprovider.User{}
@ -269,6 +269,58 @@ func TestEventRuleMatch(t *testing.T) {
}, },
}) })
assert.False(t, res) 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) { func TestDoubleStarMatching(t *testing.T) {
@ -453,6 +505,10 @@ func TestEventManagerErrors(t *testing.T) {
err = executePwdExpirationCheckRuleAction(dataprovider.EventActionPasswordExpiration{}, err = executePwdExpirationCheckRuleAction(dataprovider.EventActionPasswordExpiration{},
dataprovider.ConditionOptions{}, &EventParams{}) dataprovider.ConditionOptions{}, &EventParams{})
assert.Error(t, err) assert.Error(t, err)
_, err = executeAdminCheckAction(&dataprovider.EventActionIDPAccountCheck{}, &EventParams{})
assert.Error(t, err)
_, err = executeUserCheckAction(&dataprovider.EventActionIDPAccountCheck{}, &EventParams{})
assert.Error(t, err)
groupName := "agroup" groupName := "agroup"
err = executeQuotaResetForUser(&dataprovider.User{ err = executeQuotaResetForUser(&dataprovider.User{
@ -545,7 +601,7 @@ func TestEventManagerErrors(t *testing.T) {
}}, dataprovider.EventActionPasswordExpiration{}) }}, dataprovider.EventActionPasswordExpiration{})
assert.Error(t, err) assert.Error(t, err)
_, _, err = getHTTPRuleActionBody(dataprovider.EventActionHTTPConfig{ _, _, err = getHTTPRuleActionBody(&dataprovider.EventActionHTTPConfig{
Method: http.MethodPost, Method: http.MethodPost,
Parts: []dataprovider.HTTPPart{ Parts: []dataprovider.HTTPPart{
{ {
@ -562,7 +618,7 @@ func TestEventManagerErrors(t *testing.T) {
Type: sdk.GroupTypePrimary, Type: sdk.GroupTypePrimary,
}, },
}, },
}, &EventParams{}) }, &EventParams{}, false)
assert.Error(t, err) assert.Error(t, err)
dataRetentionAction := dataprovider.BaseEventAction{ dataRetentionAction := dataprovider.BaseEventAction{
@ -1181,11 +1237,17 @@ func TestEventRuleActions(t *testing.T) {
assert.Contains(t, err.Error(), "no folder quota reset executed") assert.Contains(t, err.Error(), "no folder quota reset executed")
} }
body, _, err := getHTTPRuleActionBody(dataprovider.EventActionHTTPConfig{ body, _, err := getHTTPRuleActionBody(&dataprovider.EventActionHTTPConfig{
Method: http.MethodPost, Method: http.MethodPost,
}, nil, nil, dataprovider.User{}, &EventParams{}) }, nil, nil, dataprovider.User{}, &EventParams{}, true)
assert.NoError(t, err) assert.NoError(t, err)
assert.Nil(t, body) 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) err = os.RemoveAll(folder1.MappedPath)
assert.NoError(t, err) assert.NoError(t, err)
@ -1195,6 +1257,99 @@ func TestEventRuleActions(t *testing.T) {
assert.NoError(t, err) 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) { func TestUserExpirationCheck(t *testing.T) {
username := "test_user_expiration_check" username := "test_user_expiration_check"
user := dataprovider.User{ user := dataprovider.User{
@ -1778,6 +1933,22 @@ func TestEventParamsCopy(t *testing.T) {
assert.Equal(t, "a_copy", paramsCopy.retentionChecks[0].ActionName) assert.Equal(t, "a_copy", paramsCopy.retentionChecks[0].ActionName)
assert.Equal(t, "p_copy", paramsCopy.retentionChecks[0].Results[0].Path) assert.Equal(t, "p_copy", paramsCopy.retentionChecks[0].Results[0].Path)
assert.Equal(t, 2, paramsCopy.retentionChecks[0].Results[0].Retention) 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) { func TestEventParamsStatusFromError(t *testing.T) {
@ -1810,14 +1981,14 @@ func TestWriteHTTPPartsError(t *testing.T) {
errTest: io.ErrShortWrite, 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) assert.ErrorIs(t, err, io.ErrShortWrite)
body := "test body" body := "test body"
m = multipart.NewWriter(&testWriter{sentinel: body}) m = multipart.NewWriter(&testWriter{sentinel: body})
err = writeHTTPPart(m, dataprovider.HTTPPart{ err = writeHTTPPart(m, dataprovider.HTTPPart{
Body: body, Body: body,
}, nil, nil, nil, &EventParams{}) }, nil, nil, nil, &EventParams{}, false)
assert.ErrorIs(t, err, io.ErrUnexpectedEOF) assert.ErrorIs(t, err, io.ErrUnexpectedEOF)
} }

View file

@ -6033,6 +6033,286 @@ func TestEventRuleRenameEvent(t *testing.T) {
require.NoError(t, err) 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) { func TestEventRuleCertificate(t *testing.T) {
smtpCfg := smtp.Config{ smtpCfg := smtp.Config{
Host: "127.0.0.1", Host: "127.0.0.1",

View file

@ -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 { return p.dbHandle.Update(func(tx *bolt.Tx) error {
bucket, err := p.getUsersBucket(tx) bucket, err := p.getUsersBucket(tx)
if err != nil { if err != nil {
@ -1031,7 +1031,7 @@ func (p *BoltProvider) dumpFolders() ([]vfs.BaseVirtualFolder, error) {
return folders, err 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) folders := make([]vfs.BaseVirtualFolder, 0, limit)
var err error var err error
if limit <= 0 { if limit <= 0 {
@ -1279,7 +1279,7 @@ func (p *BoltProvider) getUsedFolderQuota(name string) (int, int64, error) {
return folder.UsedQuotaFiles, folder.UsedQuotaSize, err 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) groups := make([]Group, 0, limit)
var err error var err error
if limit <= 0 { 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 return nil, ErrNotImplemented
} }
func (p *BoltProvider) getDefenderHostByIP(ip string, from int64) (DefenderEntry, error) { func (p *BoltProvider) getDefenderHostByIP(_ string, _ int64) (DefenderEntry, error) {
return DefenderEntry{}, ErrNotImplemented return DefenderEntry{}, ErrNotImplemented
} }
func (p *BoltProvider) isDefenderHostBanned(ip string) (DefenderEntry, error) { func (p *BoltProvider) isDefenderHostBanned(_ string) (DefenderEntry, error) {
return DefenderEntry{}, ErrNotImplemented return DefenderEntry{}, ErrNotImplemented
} }
func (p *BoltProvider) updateDefenderBanTime(ip string, minutes int) error { func (p *BoltProvider) updateDefenderBanTime(_ string, _ int) error {
return ErrNotImplemented return ErrNotImplemented
} }
func (p *BoltProvider) deleteDefenderHost(ip string) error { func (p *BoltProvider) deleteDefenderHost(_ string) error {
return ErrNotImplemented return ErrNotImplemented
} }
func (p *BoltProvider) addDefenderEvent(ip string, score int) error { func (p *BoltProvider) addDefenderEvent(_ string, _ int) error {
return ErrNotImplemented return ErrNotImplemented
} }
func (p *BoltProvider) setDefenderBanTime(ip string, banTime int64) error { func (p *BoltProvider) setDefenderBanTime(_ string, _ int64) error {
return ErrNotImplemented return ErrNotImplemented
} }
func (p *BoltProvider) cleanupDefender(from int64) error { func (p *BoltProvider) cleanupDefender(_ int64) error {
return ErrNotImplemented return ErrNotImplemented
} }
func (p *BoltProvider) addActiveTransfer(transfer ActiveTransfer) error { func (p *BoltProvider) addActiveTransfer(_ ActiveTransfer) error {
return ErrNotImplemented return ErrNotImplemented
} }
func (p *BoltProvider) updateActiveTransferSizes(ulSize, dlSize, transferID int64, connectionID string) error { func (p *BoltProvider) updateActiveTransferSizes(_, _, _ int64, _ string) error {
return ErrNotImplemented return ErrNotImplemented
} }
func (p *BoltProvider) removeActiveTransfer(transferID int64, connectionID string) error { func (p *BoltProvider) removeActiveTransfer(_ int64, _ string) error {
return ErrNotImplemented return ErrNotImplemented
} }
func (p *BoltProvider) cleanupActiveTransfers(before time.Time) error { func (p *BoltProvider) cleanupActiveTransfers(_ time.Time) error {
return ErrNotImplemented return ErrNotImplemented
} }
func (p *BoltProvider) getActiveTransfers(from time.Time) ([]ActiveTransfer, error) { func (p *BoltProvider) getActiveTransfers(_ time.Time) ([]ActiveTransfer, error) {
return nil, ErrNotImplemented return nil, ErrNotImplemented
} }
func (p *BoltProvider) addSharedSession(session Session) error { func (p *BoltProvider) addSharedSession(_ Session) error {
return ErrNotImplemented return ErrNotImplemented
} }
func (p *BoltProvider) deleteSharedSession(key string) error { func (p *BoltProvider) deleteSharedSession(_ string) error {
return ErrNotImplemented return ErrNotImplemented
} }
func (p *BoltProvider) getSharedSession(key string) (Session, error) { func (p *BoltProvider) getSharedSession(_ string) (Session, error) {
return Session{}, ErrNotImplemented return Session{}, ErrNotImplemented
} }
func (p *BoltProvider) cleanupSharedSessions(sessionType SessionType, before int64) error { func (p *BoltProvider) cleanupSharedSessions(_ SessionType, _ int64) error {
return ErrNotImplemented 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 { if limit <= 0 {
return nil, nil 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 { return p.dbHandle.Update(func(tx *bolt.Tx) error {
bucket, err := p.getRulesBucket(tx) bucket, err := p.getRulesBucket(tx)
if err != nil { 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 return Task{}, ErrNotImplemented
} }
func (*BoltProvider) addTask(name string) error { func (*BoltProvider) addTask(_ string) error {
return ErrNotImplemented return ErrNotImplemented
} }
func (*BoltProvider) updateTask(name string, version int64) error { func (*BoltProvider) updateTask(_ string, _ int64) error {
return ErrNotImplemented return ErrNotImplemented
} }
func (*BoltProvider) updateTaskTimestamp(name string) error { func (*BoltProvider) updateTaskTimestamp(_ string) error {
return ErrNotImplemented return ErrNotImplemented
} }
@ -2557,7 +2557,7 @@ func (*BoltProvider) addNode() error {
return ErrNotImplemented return ErrNotImplemented
} }
func (*BoltProvider) getNodeByName(name string) (Node, error) { func (*BoltProvider) getNodeByName(_ string) (Node, error) {
return Node{}, ErrNotImplemented 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) roles := make([]Role, 0, limit)
if limit <= 0 { if limit <= 0 {
return roles, nil 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 { return p.dbHandle.Update(func(tx *bolt.Tx) error {
bucket, err := p.getIPListsBucket(tx) bucket, err := p.getIPListsBucket(tx)
if err != nil { if err != nil {
@ -2888,7 +2888,7 @@ func (p *BoltProvider) getIPListEntries(listType IPListType, filter, from, order
return entries, err return entries, err
} }
func (p *BoltProvider) getRecentlyUpdatedIPListEntries(after int64) ([]IPListEntry, error) { func (p *BoltProvider) getRecentlyUpdatedIPListEntries(_ int64) ([]IPListEntry, error) {
return nil, ErrNotImplemented return nil, ErrNotImplemented
} }

View file

@ -47,13 +47,14 @@ const (
ActionTypeMetadataCheck ActionTypeMetadataCheck
ActionTypePasswordExpirationCheck ActionTypePasswordExpirationCheck
ActionTypeUserExpirationCheck ActionTypeUserExpirationCheck
ActionTypeIDPAccountCheck
) )
var ( var (
supportedEventActions = []int{ActionTypeHTTP, ActionTypeCommand, ActionTypeEmail, ActionTypeFilesystem, supportedEventActions = []int{ActionTypeHTTP, ActionTypeCommand, ActionTypeEmail, ActionTypeFilesystem,
ActionTypeBackup, ActionTypeUserQuotaReset, ActionTypeFolderQuotaReset, ActionTypeTransferQuotaReset, ActionTypeBackup, ActionTypeUserQuotaReset, ActionTypeFolderQuotaReset, ActionTypeTransferQuotaReset,
ActionTypeDataRetentionCheck, ActionTypeMetadataCheck, ActionTypePasswordExpirationCheck, ActionTypeDataRetentionCheck, ActionTypeMetadataCheck, ActionTypePasswordExpirationCheck,
ActionTypeUserExpirationCheck} ActionTypeUserExpirationCheck, ActionTypeIDPAccountCheck}
) )
func isActionTypeValid(action int) bool { func isActionTypeValid(action int) bool {
@ -84,6 +85,8 @@ func getActionTypeAsString(action int) string {
return "Password expiration check" return "Password expiration check"
case ActionTypeUserExpirationCheck: case ActionTypeUserExpirationCheck:
return "User expiration check" return "User expiration check"
case ActionTypeIDPAccountCheck:
return "Identity Provider account check"
default: default:
return "Command" return "Command"
} }
@ -99,11 +102,12 @@ const (
EventTriggerIPBlocked EventTriggerIPBlocked
EventTriggerCertificate EventTriggerCertificate
EventTriggerOnDemand EventTriggerOnDemand
EventTriggerIDPLogin
) )
var ( var (
supportedEventTriggers = []int{EventTriggerFsEvent, EventTriggerProviderEvent, EventTriggerSchedule, supportedEventTriggers = []int{EventTriggerFsEvent, EventTriggerProviderEvent, EventTriggerSchedule,
EventTriggerIPBlocked, EventTriggerCertificate, EventTriggerOnDemand} EventTriggerIPBlocked, EventTriggerCertificate, EventTriggerIDPLogin, EventTriggerOnDemand}
) )
func isEventTriggerValid(trigger int) bool { func isEventTriggerValid(trigger int) bool {
@ -122,11 +126,24 @@ func getTriggerTypeAsString(trigger int) string {
return "Certificate renewal" return "Certificate renewal"
case EventTriggerOnDemand: case EventTriggerOnDemand:
return "On demand" return "On demand"
case EventTriggerIDPLogin:
return "Identity Provider login"
default: default:
return "Schedule" return "Schedule"
} }
} }
// Supported IDP login events
const (
IDPLoginAny = iota
IDPLoginUser
IDPLoginAdmin
)
var (
supportedIDPLoginEvents = []int{IDPLoginAny, IDPLoginUser, IDPLoginAdmin}
)
// Supported filesystem actions // Supported filesystem actions
const ( const (
FilesystemActionRename = iota + 1 FilesystemActionRename = iota + 1
@ -276,6 +293,16 @@ type EventActionHTTPConfig struct {
Parts []HTTPPart `json:"parts,omitempty"` 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 { func (c *EventActionHTTPConfig) isTimeoutNotValid() bool {
if c.HasMultipartFiles() { if c.HasMultipartFiles() {
return false return false
@ -833,6 +860,24 @@ func (c *EventActionPasswordExpiration) validate() error {
return nil 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 // BaseEventActionOptions defines the supported configuration options for a base event actions
type BaseEventActionOptions struct { type BaseEventActionOptions struct {
HTTPConfig EventActionHTTPConfig `json:"http_config"` HTTPConfig EventActionHTTPConfig `json:"http_config"`
@ -841,6 +886,7 @@ type BaseEventActionOptions struct {
RetentionConfig EventActionDataRetentionConfig `json:"retention_config"` RetentionConfig EventActionDataRetentionConfig `json:"retention_config"`
FsConfig EventActionFilesystemConfig `json:"fs_config"` FsConfig EventActionFilesystemConfig `json:"fs_config"`
PwdExpirationConfig EventActionPasswordExpiration `json:"pwd_expiration_config"` PwdExpirationConfig EventActionPasswordExpiration `json:"pwd_expiration_config"`
IDPConfig EventActionIDPAccountCheck `json:"idp_config"`
} }
func (o *BaseEventActionOptions) getACopy() BaseEventActionOptions { func (o *BaseEventActionOptions) getACopy() BaseEventActionOptions {
@ -901,6 +947,11 @@ func (o *BaseEventActionOptions) getACopy() BaseEventActionOptions {
PwdExpirationConfig: EventActionPasswordExpiration{ PwdExpirationConfig: EventActionPasswordExpiration{
Threshold: o.PwdExpirationConfig.Threshold, Threshold: o.PwdExpirationConfig.Threshold,
}, },
IDPConfig: EventActionIDPAccountCheck{
Mode: o.IDPConfig.Mode,
TemplateUser: o.IDPConfig.TemplateUser,
TemplateAdmin: o.IDPConfig.TemplateAdmin,
},
FsConfig: o.FsConfig.getACopy(), FsConfig: o.FsConfig.getACopy(),
} }
} }
@ -933,6 +984,7 @@ func (o *BaseEventActionOptions) validate(action int, name string) error {
o.RetentionConfig = EventActionDataRetentionConfig{} o.RetentionConfig = EventActionDataRetentionConfig{}
o.FsConfig = EventActionFilesystemConfig{} o.FsConfig = EventActionFilesystemConfig{}
o.PwdExpirationConfig = EventActionPasswordExpiration{} o.PwdExpirationConfig = EventActionPasswordExpiration{}
o.IDPConfig = EventActionIDPAccountCheck{}
return o.HTTPConfig.validate(name) return o.HTTPConfig.validate(name)
case ActionTypeCommand: case ActionTypeCommand:
o.HTTPConfig = EventActionHTTPConfig{} o.HTTPConfig = EventActionHTTPConfig{}
@ -940,6 +992,7 @@ func (o *BaseEventActionOptions) validate(action int, name string) error {
o.RetentionConfig = EventActionDataRetentionConfig{} o.RetentionConfig = EventActionDataRetentionConfig{}
o.FsConfig = EventActionFilesystemConfig{} o.FsConfig = EventActionFilesystemConfig{}
o.PwdExpirationConfig = EventActionPasswordExpiration{} o.PwdExpirationConfig = EventActionPasswordExpiration{}
o.IDPConfig = EventActionIDPAccountCheck{}
return o.CmdConfig.validate() return o.CmdConfig.validate()
case ActionTypeEmail: case ActionTypeEmail:
o.HTTPConfig = EventActionHTTPConfig{} o.HTTPConfig = EventActionHTTPConfig{}
@ -947,6 +1000,7 @@ func (o *BaseEventActionOptions) validate(action int, name string) error {
o.RetentionConfig = EventActionDataRetentionConfig{} o.RetentionConfig = EventActionDataRetentionConfig{}
o.FsConfig = EventActionFilesystemConfig{} o.FsConfig = EventActionFilesystemConfig{}
o.PwdExpirationConfig = EventActionPasswordExpiration{} o.PwdExpirationConfig = EventActionPasswordExpiration{}
o.IDPConfig = EventActionIDPAccountCheck{}
return o.EmailConfig.validate() return o.EmailConfig.validate()
case ActionTypeDataRetentionCheck: case ActionTypeDataRetentionCheck:
o.HTTPConfig = EventActionHTTPConfig{} o.HTTPConfig = EventActionHTTPConfig{}
@ -954,6 +1008,7 @@ func (o *BaseEventActionOptions) validate(action int, name string) error {
o.EmailConfig = EventActionEmailConfig{} o.EmailConfig = EventActionEmailConfig{}
o.FsConfig = EventActionFilesystemConfig{} o.FsConfig = EventActionFilesystemConfig{}
o.PwdExpirationConfig = EventActionPasswordExpiration{} o.PwdExpirationConfig = EventActionPasswordExpiration{}
o.IDPConfig = EventActionIDPAccountCheck{}
return o.RetentionConfig.validate() return o.RetentionConfig.validate()
case ActionTypeFilesystem: case ActionTypeFilesystem:
o.HTTPConfig = EventActionHTTPConfig{} o.HTTPConfig = EventActionHTTPConfig{}
@ -961,6 +1016,7 @@ func (o *BaseEventActionOptions) validate(action int, name string) error {
o.EmailConfig = EventActionEmailConfig{} o.EmailConfig = EventActionEmailConfig{}
o.RetentionConfig = EventActionDataRetentionConfig{} o.RetentionConfig = EventActionDataRetentionConfig{}
o.PwdExpirationConfig = EventActionPasswordExpiration{} o.PwdExpirationConfig = EventActionPasswordExpiration{}
o.IDPConfig = EventActionIDPAccountCheck{}
return o.FsConfig.validate() return o.FsConfig.validate()
case ActionTypePasswordExpirationCheck: case ActionTypePasswordExpirationCheck:
o.HTTPConfig = EventActionHTTPConfig{} o.HTTPConfig = EventActionHTTPConfig{}
@ -968,7 +1024,16 @@ func (o *BaseEventActionOptions) validate(action int, name string) error {
o.EmailConfig = EventActionEmailConfig{} o.EmailConfig = EventActionEmailConfig{}
o.RetentionConfig = EventActionDataRetentionConfig{} o.RetentionConfig = EventActionDataRetentionConfig{}
o.FsConfig = EventActionFilesystemConfig{} o.FsConfig = EventActionFilesystemConfig{}
o.IDPConfig = EventActionIDPAccountCheck{}
return o.PwdExpirationConfig.validate() 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: default:
o.HTTPConfig = EventActionHTTPConfig{} o.HTTPConfig = EventActionHTTPConfig{}
o.CmdConfig = EventActionCommandConfig{} o.CmdConfig = EventActionCommandConfig{}
@ -976,6 +1041,7 @@ func (o *BaseEventActionOptions) validate(action int, name string) error {
o.RetentionConfig = EventActionDataRetentionConfig{} o.RetentionConfig = EventActionDataRetentionConfig{}
o.FsConfig = EventActionFilesystemConfig{} o.FsConfig = EventActionFilesystemConfig{}
o.PwdExpirationConfig = EventActionPasswordExpiration{} o.PwdExpirationConfig = EventActionPasswordExpiration{}
o.IDPConfig = EventActionIDPAccountCheck{}
} }
return nil return nil
} }
@ -1086,12 +1152,14 @@ func (a *EventAction) validateAssociation(trigger int, fsEvents []string) error
} }
} }
if a.Options.ExecuteSync { if a.Options.ExecuteSync {
if trigger != EventTriggerFsEvent { if trigger != EventTriggerFsEvent && trigger != EventTriggerIDPLogin {
return util.NewValidationError("sync execution is only supported for some filesystem events") return util.NewValidationError("sync execution is only supported for some filesystem events and Identity Provider logins")
} }
for _, ev := range fsEvents { if trigger == EventTriggerFsEvent {
if !util.Contains(allowedSyncFsEvents, ev) { for _, ev := range fsEvents {
return util.NewValidationError("sync execution is only supported for upload and pre-* events") 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 // EventConditions defines the conditions for an event rule
type EventConditions struct { type EventConditions struct {
// Only one between FsEvents, ProviderEvents and Schedule is allowed // Only one between FsEvents, ProviderEvents and Schedule is allowed
FsEvents []string `json:"fs_events,omitempty"` FsEvents []string `json:"fs_events,omitempty"`
ProviderEvents []string `json:"provider_events,omitempty"` ProviderEvents []string `json:"provider_events,omitempty"`
Schedules []Schedule `json:"schedules,omitempty"` Schedules []Schedule `json:"schedules,omitempty"`
Options ConditionOptions `json:"options"` // 0 any, 1 user, 2 admin
IDPLoginEvent int `json:"idp_login_event,omitempty"`
Options ConditionOptions `json:"options"`
} }
func (c *EventConditions) getACopy() EventConditions { func (c *EventConditions) getACopy() EventConditions {
@ -1238,16 +1308,30 @@ func (c *EventConditions) getACopy() EventConditions {
FsEvents: fsEvents, FsEvents: fsEvents,
ProviderEvents: providerEvents, ProviderEvents: providerEvents,
Schedules: schedules, Schedules: schedules,
IDPLoginEvent: c.IDPLoginEvent,
Options: c.Options.getACopy(), 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 { func (c *EventConditions) validate(trigger int) error {
switch trigger { switch trigger {
case EventTriggerFsEvent: case EventTriggerFsEvent:
c.ProviderEvents = nil c.ProviderEvents = nil
c.Schedules = nil c.Schedules = nil
c.Options.ProviderObjects = nil c.Options.ProviderObjects = nil
c.IDPLoginEvent = 0
if len(c.FsEvents) == 0 { if len(c.FsEvents) == 0 {
return util.NewValidationError("at least one filesystem event is required") 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.Protocols = nil
c.Options.MinFileSize = 0 c.Options.MinFileSize = 0
c.Options.MaxFileSize = 0 c.Options.MaxFileSize = 0
c.IDPLoginEvent = 0
if len(c.ProviderEvents) == 0 { if len(c.ProviderEvents) == 0 {
return util.NewValidationError("at least one provider event is required") 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.MinFileSize = 0
c.Options.MaxFileSize = 0 c.Options.MaxFileSize = 0
c.Options.ProviderObjects = nil c.Options.ProviderObjects = nil
if len(c.Schedules) == 0 { c.IDPLoginEvent = 0
return util.NewValidationError("at least one schedule is required") if err := c.validateSchedules(); err != nil {
} return err
for _, schedule := range c.Schedules {
if err := schedule.validate(); err != nil {
return err
}
} }
case EventTriggerIPBlocked, EventTriggerCertificate: case EventTriggerIPBlocked, EventTriggerCertificate:
c.FsEvents = nil c.FsEvents = nil
@ -1299,6 +1380,7 @@ func (c *EventConditions) validate(trigger int) error {
c.Options.MinFileSize = 0 c.Options.MinFileSize = 0
c.Options.MaxFileSize = 0 c.Options.MaxFileSize = 0
c.Schedules = nil c.Schedules = nil
c.IDPLoginEvent = 0
case EventTriggerOnDemand: case EventTriggerOnDemand:
c.FsEvents = nil c.FsEvents = nil
c.ProviderEvents = nil c.ProviderEvents = nil
@ -1308,7 +1390,21 @@ func (c *EventConditions) validate(trigger int) error {
c.Options.MaxFileSize = 0 c.Options.MaxFileSize = 0
c.Options.ProviderObjects = nil c.Options.ProviderObjects = nil
c.Schedules = nil c.Schedules = nil
c.IDPLoginEvent = 0
c.Options.ConcurrentExecution = false 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: default:
c.FsEvents = nil c.FsEvents = nil
c.ProviderEvents = nil c.ProviderEvents = nil
@ -1319,6 +1415,7 @@ func (c *EventConditions) validate(trigger int) error {
c.Options.MinFileSize = 0 c.Options.MinFileSize = 0
c.Options.MaxFileSize = 0 c.Options.MaxFileSize = 0
c.Schedules = nil c.Schedules = nil
c.IDPLoginEvent = 0
} }
return c.Options.validate() return c.Options.validate()
@ -1453,7 +1550,7 @@ func (r *EventRule) validateMandatorySyncActions() error {
} }
for _, ev := range r.Conditions.FsEvents { for _, ev := range r.Conditions.FsEvents {
if util.Contains(mandatorySyncFsEvents, ev) { 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 return nil
@ -1508,6 +1605,39 @@ func (r *EventRule) hasUserAssociated(providerObjectType string) bool {
return false 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 // CheckActionsConsistency returns an error if the actions cannot be executed
func (r *EventRule) CheckActionsConsistency(providerObjectType string) error { func (r *EventRule) CheckActionsConsistency(providerObjectType string) error {
switch r.Trigger { switch r.Trigger {
@ -1528,19 +1658,7 @@ func (r *EventRule) CheckActionsConsistency(providerObjectType string) error {
return err return err
} }
} }
for _, action := range r.Actions { return r.checkActions(providerObjectType)
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
} }
// PrepareForRendering prepares an EventRule for rendering. // PrepareForRendering prepares an EventRule for rendering.

View file

@ -431,7 +431,7 @@ func (p *MemoryProvider) updateUser(user *User) error {
return nil return nil
} }
func (p *MemoryProvider) deleteUser(user User, softDelete bool) error { func (p *MemoryProvider) deleteUser(user User, _ bool) error {
p.dbHandle.Lock() p.dbHandle.Lock()
defer p.dbHandle.Unlock() defer p.dbHandle.Unlock()
if p.dbHandle.isClosed { if p.dbHandle.isClosed {
@ -898,7 +898,7 @@ func (p *MemoryProvider) updateFolderQuota(name string, filesAdd int, sizeAdd in
return nil 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() p.dbHandle.Lock()
defer p.dbHandle.Unlock() defer p.dbHandle.Unlock()
if p.dbHandle.isClosed { 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)) 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) folders := make([]vfs.BaseVirtualFolder, 0, limit)
var err error var err error
p.dbHandle.Lock() p.dbHandle.Lock()
@ -2006,75 +2006,75 @@ func (p *MemoryProvider) updateShareLastUse(shareID string, numTokens int) error
return nil return nil
} }
func (p *MemoryProvider) getDefenderHosts(from int64, limit int) ([]DefenderEntry, error) { func (p *MemoryProvider) getDefenderHosts(_ int64, _ int) ([]DefenderEntry, error) {
return nil, ErrNotImplemented return nil, ErrNotImplemented
} }
func (p *MemoryProvider) getDefenderHostByIP(ip string, from int64) (DefenderEntry, error) { func (p *MemoryProvider) getDefenderHostByIP(_ string, _ int64) (DefenderEntry, error) {
return DefenderEntry{}, ErrNotImplemented return DefenderEntry{}, ErrNotImplemented
} }
func (p *MemoryProvider) isDefenderHostBanned(ip string) (DefenderEntry, error) { func (p *MemoryProvider) isDefenderHostBanned(_ string) (DefenderEntry, error) {
return DefenderEntry{}, ErrNotImplemented return DefenderEntry{}, ErrNotImplemented
} }
func (p *MemoryProvider) updateDefenderBanTime(ip string, minutes int) error { func (p *MemoryProvider) updateDefenderBanTime(_ string, _ int) error {
return ErrNotImplemented return ErrNotImplemented
} }
func (p *MemoryProvider) deleteDefenderHost(ip string) error { func (p *MemoryProvider) deleteDefenderHost(_ string) error {
return ErrNotImplemented return ErrNotImplemented
} }
func (p *MemoryProvider) addDefenderEvent(ip string, score int) error { func (p *MemoryProvider) addDefenderEvent(_ string, _ int) error {
return ErrNotImplemented return ErrNotImplemented
} }
func (p *MemoryProvider) setDefenderBanTime(ip string, banTime int64) error { func (p *MemoryProvider) setDefenderBanTime(_ string, _ int64) error {
return ErrNotImplemented return ErrNotImplemented
} }
func (p *MemoryProvider) cleanupDefender(from int64) error { func (p *MemoryProvider) cleanupDefender(_ int64) error {
return ErrNotImplemented return ErrNotImplemented
} }
func (p *MemoryProvider) addActiveTransfer(transfer ActiveTransfer) error { func (p *MemoryProvider) addActiveTransfer(_ ActiveTransfer) error {
return ErrNotImplemented return ErrNotImplemented
} }
func (p *MemoryProvider) updateActiveTransferSizes(ulSize, dlSize, transferID int64, connectionID string) error { func (p *MemoryProvider) updateActiveTransferSizes(_, _, _ int64, _ string) error {
return ErrNotImplemented return ErrNotImplemented
} }
func (p *MemoryProvider) removeActiveTransfer(transferID int64, connectionID string) error { func (p *MemoryProvider) removeActiveTransfer(_ int64, _ string) error {
return ErrNotImplemented return ErrNotImplemented
} }
func (p *MemoryProvider) cleanupActiveTransfers(before time.Time) error { func (p *MemoryProvider) cleanupActiveTransfers(_ time.Time) error {
return ErrNotImplemented return ErrNotImplemented
} }
func (p *MemoryProvider) getActiveTransfers(from time.Time) ([]ActiveTransfer, error) { func (p *MemoryProvider) getActiveTransfers(_ time.Time) ([]ActiveTransfer, error) {
return nil, ErrNotImplemented return nil, ErrNotImplemented
} }
func (p *MemoryProvider) addSharedSession(session Session) error { func (p *MemoryProvider) addSharedSession(_ Session) error {
return ErrNotImplemented return ErrNotImplemented
} }
func (p *MemoryProvider) deleteSharedSession(key string) error { func (p *MemoryProvider) deleteSharedSession(_ string) error {
return ErrNotImplemented return ErrNotImplemented
} }
func (p *MemoryProvider) getSharedSession(key string) (Session, error) { func (p *MemoryProvider) getSharedSession(_ string) (Session, error) {
return Session{}, ErrNotImplemented return Session{}, ErrNotImplemented
} }
func (p *MemoryProvider) cleanupSharedSessions(sessionType SessionType, before int64) error { func (p *MemoryProvider) cleanupSharedSessions(_ SessionType, _ int64) error {
return ErrNotImplemented 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() p.dbHandle.Lock()
defer p.dbHandle.Unlock() defer p.dbHandle.Unlock()
if p.dbHandle.isClosed { if p.dbHandle.isClosed {
@ -2395,7 +2395,7 @@ func (p *MemoryProvider) updateEventRule(rule *EventRule) error {
return nil return nil
} }
func (p *MemoryProvider) deleteEventRule(rule EventRule, softDelete bool) error { func (p *MemoryProvider) deleteEventRule(rule EventRule, _ bool) error {
p.dbHandle.Lock() p.dbHandle.Lock()
defer p.dbHandle.Unlock() defer p.dbHandle.Unlock()
if p.dbHandle.isClosed { if p.dbHandle.isClosed {
@ -2420,19 +2420,19 @@ func (p *MemoryProvider) deleteEventRule(rule EventRule, softDelete bool) error
return nil return nil
} }
func (*MemoryProvider) getTaskByName(name string) (Task, error) { func (*MemoryProvider) getTaskByName(_ string) (Task, error) {
return Task{}, ErrNotImplemented return Task{}, ErrNotImplemented
} }
func (*MemoryProvider) addTask(name string) error { func (*MemoryProvider) addTask(_ string) error {
return ErrNotImplemented return ErrNotImplemented
} }
func (*MemoryProvider) updateTask(name string, version int64) error { func (*MemoryProvider) updateTask(_ string, _ int64) error {
return ErrNotImplemented return ErrNotImplemented
} }
func (*MemoryProvider) updateTaskTimestamp(name string) error { func (*MemoryProvider) updateTaskTimestamp(_ string) error {
return ErrNotImplemented return ErrNotImplemented
} }
@ -2440,7 +2440,7 @@ func (*MemoryProvider) addNode() error {
return ErrNotImplemented return ErrNotImplemented
} }
func (*MemoryProvider) getNodeByName(name string) (Node, error) { func (*MemoryProvider) getNodeByName(_ string) (Node, error) {
return Node{}, ErrNotImplemented return Node{}, ErrNotImplemented
} }
@ -2550,7 +2550,7 @@ func (p *MemoryProvider) deleteRole(role Role) error {
return nil 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() p.dbHandle.Lock()
defer p.dbHandle.Unlock() defer p.dbHandle.Unlock()
@ -2662,7 +2662,7 @@ func (p *MemoryProvider) updateIPListEntry(entry *IPListEntry) error {
return nil 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 { if err := entry.validate(); err != nil {
return err return err
} }
@ -2721,7 +2721,7 @@ func (p *MemoryProvider) getIPListEntries(listType IPListType, filter, from, ord
return entries, nil return entries, nil
} }
func (p *MemoryProvider) getRecentlyUpdatedIPListEntries(after int64) ([]IPListEntry, error) { func (p *MemoryProvider) getRecentlyUpdatedIPListEntries(_ int64) ([]IPListEntry, error) {
return nil, ErrNotImplemented return nil, ErrNotImplemented
} }
@ -3293,7 +3293,7 @@ func (p *MemoryProvider) migrateDatabase() error {
return ErrNoInitRequired 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") return errors.New("memory provider does not store data, revert not possible")
} }

View file

@ -3491,7 +3491,7 @@ func sqlCommonUpdateEventAction(action *BaseEventAction, dbHandle *sql.DB) error
return err return err
} }
q = getUpdateRulesTimestampQuery() 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 return err
}) })
} }

View file

@ -603,7 +603,7 @@ func (*SQLiteProvider) addNode() error {
return ErrNotImplemented return ErrNotImplemented
} }
func (*SQLiteProvider) getNodeByName(name string) (Node, error) { func (*SQLiteProvider) getNodeByName(_ string) (Node, error) {
return Node{}, ErrNotImplemented return Node{}, ErrNotImplemented
} }

View file

@ -1106,8 +1106,8 @@ func getClearRuleActionMappingQuery() string {
} }
func getUpdateRulesTimestampQuery() 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)`, 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, sqlPlaceholders[1]) sqlTableEventsRules, sqlPlaceholders[0], sqlTableRulesActionsMapping, sqlTableEventsActions, sqlPlaceholders[1])
} }
func getRelatedActionsForRulesQuery(rules []EventRule) string { func getRelatedActionsForRulesQuery(rules []EventRule) string {

View file

@ -90,29 +90,29 @@ func (c *Connection) GetCommand() string {
} }
// Create is not implemented we use ClientDriverExtentionFileTransfer // 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 return nil, errNotImplemented
} }
// Mkdir creates a directory using the connection filesystem // 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() c.UpdateLastActivity()
return c.CreateDir(name, true) return c.CreateDir(name, true)
} }
// MkdirAll is not implemented, we don't need it // 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 return errNotImplemented
} }
// Open is not implemented we use ClientDriverExtentionFileTransfer and ClientDriverExtensionFileList // 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 return nil, errNotImplemented
} }
// OpenFile is not implemented we use ClientDriverExtentionFileTransfer // 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 return nil, errNotImplemented
} }
@ -140,7 +140,7 @@ func (c *Connection) Remove(name string) error {
} }
// RemoveAll is not implemented, we don't need it // RemoveAll is not implemented, we don't need it
func (c *Connection) RemoveAll(path string) error { func (c *Connection) RemoveAll(_ string) error {
return errNotImplemented return errNotImplemented
} }
@ -178,7 +178,7 @@ func (c *Connection) Name() string {
} }
// Chown changes the uid and gid of the named file // 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() c.UpdateLastActivity()
return common.ErrOpUnsupported return common.ErrOpUnsupported
@ -270,7 +270,7 @@ func (c *Connection) GetAvailableSpace(dirName string) (int64, error) {
} }
// AllocateSpace implements ClientDriverExtensionAllocate interface // AllocateSpace implements ClientDriverExtensionAllocate interface
func (c *Connection) AllocateSpace(size int) error { func (c *Connection) AllocateSpace(_ int) error {
c.UpdateLastActivity() c.UpdateLastActivity()
// we treat ALLO as NOOP see RFC 959 // we treat ALLO as NOOP see RFC 959
return nil return nil

View file

@ -282,11 +282,11 @@ func (cc mockFTPClientContext) Path() string {
return "" 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 { func (cc mockFTPClientContext) Debug() bool {
return false return false
@ -328,7 +328,7 @@ func (cc mockFTPClientContext) HasTLSForTransfers() bool {
return false return false
} }
func (cc mockFTPClientContext) SetTLSRequirement(requirement ftpserver.TLSRequirement) error { func (cc mockFTPClientContext) SetTLSRequirement(_ ftpserver.TLSRequirement) error {
return nil return nil
} }
@ -380,7 +380,7 @@ func (fs MockOsFs) Lstat(name string) (os.FileInfo, error) {
} }
// Remove removes the named file or (empty) directory. // 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 { if fs.err != nil {
return fs.err return fs.err
} }

View file

@ -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)) 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 return err
} }
@ -423,7 +423,7 @@ func RestoreAdmins(admins []dataprovider.Admin, inputFile string, mode int, exec
} }
// RestoreConfigs restores the specified provider configs // 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, executorRole string,
) error { ) error {
if configs == nil { if configs == nil {

View file

@ -298,6 +298,7 @@ func (c *jwtTokenClaims) removeCookie(w http.ResponseWriter, r *http.Request, co
Secure: isTLS(r), Secure: isTLS(r),
SameSite: http.SameSiteStrictMode, SameSite: http.SameSiteStrictMode,
}) })
w.Header().Add("Cache-Control", `no-cache="Set-Cookie"`)
invalidateToken(r) invalidateToken(r)
} }

View file

@ -35,6 +35,7 @@ func setFlashMessage(w http.ResponseWriter, r *http.Request, value string) {
Secure: isTLS(r), Secure: isTLS(r),
SameSite: http.SameSiteLaxMode, SameSite: http.SameSiteLaxMode,
}) })
w.Header().Add("Cache-Control", `no-cache="Set-Cookie"`)
} }
func getFlashMessage(w http.ResponseWriter, r *http.Request) string { func getFlashMessage(w http.ResponseWriter, r *http.Request) string {

View file

@ -315,15 +315,15 @@ func (t *throttledReader) HasSizeLimit() bool {
return false return false
} }
func (t *throttledReader) Truncate(fsPath string, size int64) (int64, error) { func (t *throttledReader) Truncate(_ string, _ int64) (int64, error) {
return 0, vfs.ErrVfsUnsupported return 0, vfs.ErrVfsUnsupported
} }
func (t *throttledReader) GetRealFsPath(fsPath string) string { func (t *throttledReader) GetRealFsPath(_ string) string {
return "" 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 return false
} }

View file

@ -1973,6 +1973,58 @@ func TestOnDemandEventRules(t *testing.T) {
assert.NoError(t, err) 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) { func TestEventActionValidation(t *testing.T) {
action := dataprovider.BaseEventAction{ action := dataprovider.BaseEventAction{
Name: "", Name: "",
@ -2235,6 +2287,15 @@ func TestEventActionValidation(t *testing.T) {
_, resp, err = httpdtest.AddEventAction(action, http.StatusBadRequest) _, resp, err = httpdtest.AddEventAction(action, http.StatusBadRequest)
assert.NoError(t, err) assert.NoError(t, err)
assert.Contains(t, string(resp), "threshold must be greater than 0") 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) { func TestEventRuleValidation(t *testing.T) {
@ -2367,7 +2428,7 @@ func TestEventRuleValidation(t *testing.T) {
} }
_, resp, err = httpdtest.AddEventRule(rule, http.StatusBadRequest) _, resp, err = httpdtest.AddEventRule(rule, http.StatusBadRequest)
assert.NoError(t, err) 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{ rule.Actions = []dataprovider.EventAction{
{ {
BaseEventAction: dataprovider.BaseEventAction{ BaseEventAction: dataprovider.BaseEventAction{
@ -2431,6 +2492,11 @@ func TestEventRuleValidation(t *testing.T) {
} }
_, resp, err = httpdtest.AddEventRule(rule, http.StatusInternalServerError) _, resp, err = httpdtest.AddEventRule(rule, http.StatusInternalServerError)
assert.NoError(t, err, string(resp)) 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) { func TestUserTransferLimits(t *testing.T) {
@ -21532,6 +21598,26 @@ func TestWebEventAction(t *testing.T) {
assert.Equal(t, 0, actionGet.Options.CmdConfig.Timeout) assert.Equal(t, 0, actionGet.Options.CmdConfig.Timeout)
assert.Len(t, actionGet.Options.CmdConfig.EnvVars, 0) 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) req, err = http.NewRequest(http.MethodDelete, path.Join(webAdminEventActionPath, action.Name), nil)
assert.NoError(t, err) assert.NoError(t, err)
setBearerForReq(req, apiToken) 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].Name, ruleGet.Actions[0].Name)
assert.Equal(t, rule.Actions[0].Order, ruleGet.Actions[0].Order) 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 // update a missing rule
req, err = http.NewRequest(http.MethodPost, path.Join(webAdminEventRulePath, rule.Name+"1"), req, err = http.NewRequest(http.MethodPost, path.Join(webAdminEventRulePath, rule.Name+"1"),
bytes.NewBuffer([]byte(form.Encode()))) bytes.NewBuffer([]byte(form.Encode())))

View file

@ -292,11 +292,11 @@ var (
type failingWriter struct { 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") return 0, errors.New("write error")
} }
func (r *failingWriter) WriteHeader(statusCode int) {} func (r *failingWriter) WriteHeader(_ int) {}
func (r *failingWriter) Header() http.Header { func (r *failingWriter) Header() http.Header {
return make(http.Header) return make(http.Header)

View file

@ -410,47 +410,70 @@ func (t *oidcToken) refreshUser(r *http.Request) error {
} }
func (t *oidcToken) getUser(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() { if t.isAdmin() {
admin, err := dataprovider.AdminExists(t.Username) params.Event = common.IDPLoginAdmin
_, admin, err := common.HandleIDPLoginEvent(params, t.CustomFields)
if err != nil { if err != nil {
return err 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 return err
} }
t.Permissions = admin.Permissions t.Permissions = admin.Permissions
t.TokenRole = admin.Role t.TokenRole = admin.Role
t.HideUserPageSections = admin.Filters.Preferences.HideUserPageSections t.HideUserPageSections = admin.Filters.Preferences.HideUserPageSections
dataprovider.UpdateAdminLastLogin(&admin) dataprovider.UpdateAdminLastLogin(admin)
return nil return nil
} }
ipAddr := util.GetIPFromRemoteAddress(r.RemoteAddr) params.Event = common.IDPLoginUser
user, err := dataprovider.GetUserAfterIDPAuth(t.Username, ipAddr, common.ProtocolOIDC, t.CustomFields) user, _, err := common.HandleIDPLoginEvent(params, t.CustomFields)
if err != nil { if err != nil {
return err 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 { 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) return fmt.Errorf("access denied: %w", err)
} }
if err := user.CheckLoginConditions(); err != nil { if err := user.CheckLoginConditions(); err != nil {
updateLoginMetrics(&user, dataprovider.LoginMethodIDP, ipAddr, err) updateLoginMetrics(user, dataprovider.LoginMethodIDP, ipAddr, err)
return err return err
} }
connectionID := fmt.Sprintf("%v_%v", common.ProtocolOIDC, xid.New().String()) connectionID := fmt.Sprintf("%s_%s", common.ProtocolOIDC, xid.New().String())
if err := checkHTTPClientUser(&user, r, connectionID, true); err != nil { if err := checkHTTPClientUser(user, r, connectionID, true); err != nil {
updateLoginMetrics(&user, dataprovider.LoginMethodIDP, ipAddr, err) updateLoginMetrics(user, dataprovider.LoginMethodIDP, ipAddr, err)
return err return err
} }
defer user.CloseFs() //nolint:errcheck defer user.CloseFs() //nolint:errcheck
err = user.CheckFsRoot(connectionID) err = user.CheckFsRoot(connectionID)
if err != nil { if err != nil {
logger.Warn(logSender, connectionID, "unable to check fs root: %v", err) 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 return err
} }
updateLoginMetrics(&user, dataprovider.LoginMethodIDP, ipAddr, nil) updateLoginMetrics(user, dataprovider.LoginMethodIDP, ipAddr, nil)
dataprovider.UpdateLastLogin(&user) dataprovider.UpdateLastLogin(user)
t.Permissions = user.Filters.WebClient t.Permissions = user.Filters.WebClient
t.TokenRole = user.Role t.TokenRole = user.Role
return nil return nil

View file

@ -66,15 +66,15 @@ type mockOAuth2Config struct {
err error err error
} }
func (c *mockOAuth2Config) AuthCodeURL(state string, opts ...oauth2.AuthCodeOption) string { func (c *mockOAuth2Config) AuthCodeURL(_ string, _ ...oauth2.AuthCodeOption) string {
return c.authCodeURL 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 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 return c.tokenSource
} }
@ -83,7 +83,7 @@ type mockOIDCVerifier struct {
err error 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 return v.token, v.err
} }
@ -1130,6 +1130,180 @@ func TestMemoryOIDCManager(t *testing.T) {
require.Len(t, oidcMgr.tokens, 0) 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) { func TestOIDCPreLoginHook(t *testing.T) {
if runtime.GOOS == osWindows { if runtime.GOOS == osWindows {
t.Skip("this test is not available on Windows") t.Skip("this test is not available on Windows")

View file

@ -2280,6 +2280,10 @@ func getEventActionOptionsFromPostFields(r *http.Request) (dataprovider.BaseEven
if r.Form.Get("cmd_arguments") != "" { if r.Form.Get("cmd_arguments") != "" {
cmdArgs = getSliceFromDelimitedValues(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{ options := dataprovider.BaseEventActionOptions{
HTTPConfig: dataprovider.EventActionHTTPConfig{ HTTPConfig: dataprovider.EventActionHTTPConfig{
Endpoint: r.Form.Get("http_endpoint"), Endpoint: r.Form.Get("http_endpoint"),
@ -2323,6 +2327,11 @@ func getEventActionOptionsFromPostFields(r *http.Request) (dataprovider.BaseEven
PwdExpirationConfig: dataprovider.EventActionPasswordExpiration{ PwdExpirationConfig: dataprovider.EventActionPasswordExpiration{
Threshold: pwdExpirationThreshold, 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 return options, nil
} }
@ -2349,6 +2358,17 @@ func getEventActionFromPostFields(r *http.Request) (dataprovider.BaseEventAction
return action, nil 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) { func getEventRuleConditionsFromPostFields(r *http.Request) (dataprovider.EventConditions, error) {
var schedules []dataprovider.Schedule var schedules []dataprovider.Schedule
var names, groupNames, roleNames, fsPaths []dataprovider.ConditionPattern var names, groupNames, roleNames, fsPaths []dataprovider.ConditionPattern
@ -2424,6 +2444,7 @@ func getEventRuleConditionsFromPostFields(r *http.Request) (dataprovider.EventCo
conditions := dataprovider.EventConditions{ conditions := dataprovider.EventConditions{
FsEvents: r.Form["fs_events"], FsEvents: r.Form["fs_events"],
ProviderEvents: r.Form["provider_events"], ProviderEvents: r.Form["provider_events"],
IDPLoginEvent: getIDPLoginEventFromPostField(r),
Schedules: schedules, Schedules: schedules,
Options: dataprovider.ConditionOptions{ Options: dataprovider.ConditionOptions{
Names: names, Names: names,

View file

@ -1596,6 +1596,9 @@ func checkEventAction(expected, actual dataprovider.BaseEventAction) error {
if expected.Options.PwdExpirationConfig.Threshold != actual.Options.PwdExpirationConfig.Threshold { if expected.Options.PwdExpirationConfig.Threshold != actual.Options.PwdExpirationConfig.Threshold {
return errors.New("password expiration threshold mismatch") 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 { if err := compareEventActionCmdConfigFields(expected.Options.CmdConfig, actual.Options.CmdConfig); err != nil {
return err return err
} }
@ -1707,6 +1710,9 @@ func checkEventConditions(expected, actual dataprovider.EventConditions) error {
if err := checkEventConditionOptions(expected.Options, actual.Options); err != nil { if err := checkEventConditionOptions(expected.Options, actual.Options); err != nil {
return err return err
} }
if expected.IDPLoginEvent != actual.IDPLoginEvent {
return errors.New("IDP login event mismatch")
}
return checkEventSchedules(expected.Schedules, actual.Schedules) return checkEventSchedules(expected.Schedules, actual.Schedules)
} }
@ -2707,6 +2713,19 @@ func compareEventActionFsConfigFields(expected, actual dataprovider.EventActionF
return compareEventActionFsCompressFields(expected.Compress, actual.Compress) 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 { func compareEventActionCmdConfigFields(expected, actual dataprovider.EventActionCommandConfig) error {
if expected.Cmd != actual.Cmd { if expected.Cmd != actual.Cmd {
return errors.New("command mismatch") return errors.New("command mismatch")

View file

@ -38,7 +38,7 @@ func init() {
RegisterSecretProvider(sdkkms.SchemeBuiltin, sdkkms.SecretStatusAES256GCM, newBuiltinSecret) RegisterSecretProvider(sdkkms.SchemeBuiltin, sdkkms.SecretStatusAES256GCM, newBuiltinSecret)
} }
func newBuiltinSecret(base BaseSecret, url, masterKey string) SecretProvider { func newBuiltinSecret(base BaseSecret, _, _ string) SecretProvider {
return &builtinSecret{ return &builtinSecret{
BaseSecret: base, BaseSecret: base,
} }

View file

@ -36,7 +36,7 @@ type localSecret struct {
} }
// NewLocalSecret returns a SecretProvider that use a locally provided symmetric key // 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{ return &localSecret{
BaseSecret: base, BaseSecret: base,
masterKey: masterKey, masterKey: masterKey,

View file

@ -81,11 +81,11 @@ func (l *HCLogAdapter) Named(name string) hclog.Logger {
} }
// StandardLogger returns a value that conforms to the stdlib log.Logger interface // 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) return log.New(&StdLoggerWrapper{Sender: l.Name()}, "", 0)
} }
// StandardWriter returns a value that conforms to io.Writer, which can be passed into log.SetOutput() // 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()} return &StdLoggerWrapper{Sender: l.Name()}
} }

View file

@ -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 // 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) metric.HTTPRequestServed(status)
l.Logger.Info(). l.Logger.Info().
Timestamp(). Timestamp().

View file

@ -338,7 +338,7 @@ func (m *Manager) SetModificationTime(storageID, objectPath string, mTime int64)
} }
// GetModificationTime returns the modification time for the specified path // 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 { if !m.hasMetadater {
return 0, ErrNoMetadater return 0, ErrNoMetadater
} }

View file

@ -351,7 +351,7 @@ func (s *Service) LoadInitialData() error {
} }
func (s *Service) restoreDump(dump *dataprovider.BackupData) 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 { if err != nil {
return fmt.Errorf("unable to restore configs from file %q: %v", s.LoadDataFrom, err) return fmt.Errorf("unable to restore configs from file %q: %v", s.LoadDataFrom, err)
} }

View file

@ -83,7 +83,7 @@ func (c *MockChannel) CloseWrite() error {
return nil 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 return true, nil
} }
@ -131,7 +131,7 @@ func (fs MockOsFs) Lstat(name string) (os.FileInfo, error) {
} }
// Remove removes the named file or (empty) directory. // 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 { if fs.err != nil {
return fs.err return fs.err
} }

View file

@ -40,7 +40,7 @@ type failingReader struct {
errRead error 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 return 0, r.errRead
} }

View file

@ -30,6 +30,7 @@ import (
"github.com/drakkan/sftpgo/v2/internal/dataprovider" "github.com/drakkan/sftpgo/v2/internal/dataprovider"
"github.com/drakkan/sftpgo/v2/internal/logger" "github.com/drakkan/sftpgo/v2/internal/logger"
"github.com/drakkan/sftpgo/v2/internal/util" "github.com/drakkan/sftpgo/v2/internal/util"
"github.com/drakkan/sftpgo/v2/internal/version"
) )
const ( const (
@ -287,7 +288,9 @@ func (c *Config) getMailClientOptions() []mail.Option {
func (c *Config) getSMTPClientAndMsg(to []string, subject, body string, contentType EmailContentType, func (c *Config) getSMTPClientAndMsg(to []string, subject, body string, contentType EmailContentType,
attachments ...*mail.File) (*mail.Client, *mail.Msg, error) { attachments ...*mail.File) (*mail.Client, *mail.Msg, error) {
version := version.Get()
msg := mail.NewMsg() msg := mail.NewMsg()
msg.SetUserAgent(fmt.Sprintf("SFTPGo-%s-%s", version.Version, version.CommitHash))
var from string var from string
if c.From != "" { if c.From != "" {

View file

@ -24,6 +24,7 @@ import (
"crypto/rsa" "crypto/rsa"
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"encoding/json"
"encoding/pem" "encoding/pem"
"errors" "errors"
"fmt" "fmt"
@ -589,7 +590,7 @@ func HTTPListenAndServe(srv *http.Server, address string, port int, isTLS bool,
return err 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() defer listener.Close()
@ -834,3 +835,15 @@ func GetLastIPForPrefix(p netip.Prefix) netip.Addr {
} }
return netip.AddrFrom16(a16) // doesn't unmap 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])
}

View file

@ -326,27 +326,27 @@ func (fs *AzureBlobFs) Mkdir(name string) error {
} }
// Symlink creates source as a symbolic link to target. // Symlink creates source as a symbolic link to target.
func (*AzureBlobFs) Symlink(source, target string) error { func (*AzureBlobFs) Symlink(_, _ string) error {
return ErrVfsUnsupported return ErrVfsUnsupported
} }
// Readlink returns the destination of the named symbolic link // Readlink returns the destination of the named symbolic link
func (*AzureBlobFs) Readlink(name string) (string, error) { func (*AzureBlobFs) Readlink(_ string) (string, error) {
return "", ErrVfsUnsupported return "", ErrVfsUnsupported
} }
// Chown changes the numeric uid and gid of the named file. // 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 return ErrVfsUnsupported
} }
// Chmod changes the mode of the named file to mode. // 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 return ErrVfsUnsupported
} }
// Chtimes changes the access and modification times of the named file. // 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() { if !plugin.Handler.HasMetadater() {
return ErrVfsUnsupported 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 changes the size of the named file.
// Truncate by path is not supported, while truncating an opened // Truncate by path is not supported, while truncating an opened
// file is handled inside base transfer // file is handled inside base transfer
func (*AzureBlobFs) Truncate(name string, size int64) error { func (*AzureBlobFs) Truncate(_ string, _ int64) error {
return ErrVfsUnsupported 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. // GetAtomicUploadPath returns the path to use for an atomic upload.
// Azure Blob Storage uploads are already atomic, we never call this method // Azure Blob Storage uploads are already atomic, we never call this method
func (*AzureBlobFs) GetAtomicUploadPath(name string) string { func (*AzureBlobFs) GetAtomicUploadPath(_ string) string {
return "" return ""
} }
@ -702,7 +702,7 @@ func (fs *AzureBlobFs) ResolvePath(virtualPath string) (string, error) {
} }
// CopyFile implements the FsFileCopier interface // 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) return fs.copyFileInternal(source, target)
} }
@ -731,7 +731,7 @@ func (*AzureBlobFs) Close() error {
} }
// GetAvailableDiskSize returns the available size for the specified path // 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 return nil, ErrStorageSizeUnavailable
} }

View file

@ -206,7 +206,7 @@ func (fs *CryptFs) Create(name string, flag int) (File, *PipeWriter, func(), err
} }
// Truncate changes the size of the named file // Truncate changes the size of the named file
func (*CryptFs) Truncate(name string, size int64) error { func (*CryptFs) Truncate(_ string, _ int64) error {
return ErrVfsUnsupported return ErrVfsUnsupported
} }

View file

@ -284,27 +284,27 @@ func (fs *GCSFs) Mkdir(name string) error {
} }
// Symlink creates source as a symbolic link to target. // Symlink creates source as a symbolic link to target.
func (*GCSFs) Symlink(source, target string) error { func (*GCSFs) Symlink(_, _ string) error {
return ErrVfsUnsupported return ErrVfsUnsupported
} }
// Readlink returns the destination of the named symbolic link // Readlink returns the destination of the named symbolic link
func (*GCSFs) Readlink(name string) (string, error) { func (*GCSFs) Readlink(_ string) (string, error) {
return "", ErrVfsUnsupported return "", ErrVfsUnsupported
} }
// Chown changes the numeric uid and gid of the named file. // 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 return ErrVfsUnsupported
} }
// Chmod changes the mode of the named file to mode. // 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 return ErrVfsUnsupported
} }
// Chtimes changes the access and modification times of the named file. // 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() { if !plugin.Handler.HasMetadater() {
return ErrVfsUnsupported 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 changes the size of the named file.
// Truncate by path is not supported, while truncating an opened // Truncate by path is not supported, while truncating an opened
// file is handled inside base transfer // file is handled inside base transfer
func (*GCSFs) Truncate(name string, size int64) error { func (*GCSFs) Truncate(_ string, _ int64) error {
return ErrVfsUnsupported 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. // GetAtomicUploadPath returns the path to use for an atomic upload.
// GCS uploads are already atomic, we never call this method for GCS // GCS uploads are already atomic, we never call this method for GCS
func (*GCSFs) GetAtomicUploadPath(name string) string { func (*GCSFs) GetAtomicUploadPath(_ string) string {
return "" return ""
} }
@ -688,7 +688,7 @@ func (fs *GCSFs) ResolvePath(virtualPath string) (string, error) {
} }
// CopyFile implements the FsFileCopier interface // 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) return fs.copyFileInternal(source, target)
} }
@ -904,7 +904,7 @@ func (fs *GCSFs) Close() error {
} }
// GetAvailableDiskSize returns the available size for the specified path // 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 return nil, ErrStorageSizeUnavailable
} }

View file

@ -385,7 +385,7 @@ func (fs *HTTPFs) Rename(source, target string) (int, int64, error) {
} }
// Remove removes the named file or (empty) directory. // 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)) ctx, cancelFn := context.WithDeadline(context.Background(), time.Now().Add(fs.ctxTimeout))
defer cancelFn() defer cancelFn()
@ -411,17 +411,17 @@ func (fs *HTTPFs) Mkdir(name string) error {
} }
// Symlink creates source as a symbolic link to target. // Symlink creates source as a symbolic link to target.
func (*HTTPFs) Symlink(source, target string) error { func (*HTTPFs) Symlink(_, _ string) error {
return ErrVfsUnsupported return ErrVfsUnsupported
} }
// Readlink returns the destination of the named symbolic link // Readlink returns the destination of the named symbolic link
func (*HTTPFs) Readlink(name string) (string, error) { func (*HTTPFs) Readlink(_ string) (string, error) {
return "", ErrVfsUnsupported return "", ErrVfsUnsupported
} }
// Chown changes the numeric uid and gid of the named file. // 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 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. // 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)) ctx, cancelFn := context.WithDeadline(context.Background(), time.Now().Add(fs.ctxTimeout))
defer cancelFn() 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. // GetAtomicUploadPath returns the path to use for an atomic upload.
func (*HTTPFs) GetAtomicUploadPath(name string) string { func (*HTTPFs) GetAtomicUploadPath(_ string) string {
return "" return ""
} }

View file

@ -140,7 +140,7 @@ func (fs *OsFs) Rename(source, target string) (int, int64, error) {
} }
// Remove removes the named file or (empty) directory. // 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) 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 // 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) return os.Chtimes(name, atime, mtime)
} }

View file

@ -337,27 +337,27 @@ func (fs *S3Fs) Mkdir(name string) error {
} }
// Symlink creates source as a symbolic link to target. // Symlink creates source as a symbolic link to target.
func (*S3Fs) Symlink(source, target string) error { func (*S3Fs) Symlink(_, _ string) error {
return ErrVfsUnsupported return ErrVfsUnsupported
} }
// Readlink returns the destination of the named symbolic link // Readlink returns the destination of the named symbolic link
func (*S3Fs) Readlink(name string) (string, error) { func (*S3Fs) Readlink(_ string) (string, error) {
return "", ErrVfsUnsupported return "", ErrVfsUnsupported
} }
// Chown changes the numeric uid and gid of the named file. // 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 return ErrVfsUnsupported
} }
// Chmod changes the mode of the named file to mode. // 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 return ErrVfsUnsupported
} }
// Chtimes changes the access and modification times of the named file. // 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() { if !plugin.Handler.HasMetadater() {
return ErrVfsUnsupported 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 changes the size of the named file.
// Truncate by path is not supported, while truncating an opened // Truncate by path is not supported, while truncating an opened
// file is handled inside base transfer // file is handled inside base transfer
func (*S3Fs) Truncate(name string, size int64) error { func (*S3Fs) Truncate(_ string, _ int64) error {
return ErrVfsUnsupported 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. // GetAtomicUploadPath returns the path to use for an atomic upload.
// S3 uploads are already atomic, we never call this method for S3 // S3 uploads are already atomic, we never call this method for S3
func (*S3Fs) GetAtomicUploadPath(name string) string { func (*S3Fs) GetAtomicUploadPath(_ string) string {
return "" return ""
} }
@ -1009,7 +1009,7 @@ func (*S3Fs) Close() error {
} }
// GetAvailableDiskSize returns the available size for the specified path // 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 return nil, ErrStorageSizeUnavailable
} }

View file

@ -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. // 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() client, err := fs.conn.getClient()
if err != nil { if err != nil {
return err return err

View file

@ -27,6 +27,6 @@ func isCrossDeviceError(err error) bool {
return errors.Is(err, unix.EXDEV) return errors.Is(err, unix.EXDEV)
} }
func isInvalidNameError(err error) bool { func isInvalidNameError(_ error) bool {
return false return false
} }

View file

@ -82,7 +82,7 @@ type webDavFileInfo struct {
} }
// ContentType implements webdav.ContentTyper interface // 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) extension := path.Ext(fi.virtualPath)
if ctype, ok := customMimeTypeMapping[extension]; ok { if ctype, ok := customMimeTypeMapping[extension]; ok {
return ctype, nil return ctype, nil
@ -107,7 +107,7 @@ func (fi *webDavFileInfo) ContentType(ctx context.Context) (string, error) {
} }
// Readdir reads directory entries from the handle // 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()) { if !f.Connection.User.HasPerm(dataprovider.PermListItems, f.GetVirtualPath()) {
return nil, f.Connection.GetPermissionDeniedError() return nil, f.Connection.GetPermissionDeniedError()
} }

View file

@ -85,7 +85,7 @@ func (c *Connection) GetCommand() string {
} }
// Mkdir creates a directory using the connection filesystem // 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() c.UpdateLastActivity()
name = util.CleanPath(name) 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 // 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() c.UpdateLastActivity()
oldName = util.CleanPath(oldName) 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, // Stat returns a FileInfo describing the named file/directory, or an error,
// if any happens // 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() c.UpdateLastActivity()
name = util.CleanPath(name) 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. // RemoveAll removes path and any children it contains.
// If the path does not exist, RemoveAll returns nil (no error). // 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() c.UpdateLastActivity()
name = util.CleanPath(name) 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. // OpenFile opens the named file with specified flag.
// This method is used for uploads and downloads but also for Stat and Readdir // 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() c.UpdateLastActivity()
name = util.CleanPath(name) name = util.CleanPath(name)

View file

@ -303,7 +303,7 @@ func (fs *MockOsFs) IsAtomicUploadSupported() bool {
} }
// Remove removes the named file or (empty) directory. // 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 { if fs.err != nil {
return fs.err return fs.err
} }
@ -317,7 +317,7 @@ func (fs *MockOsFs) Rename(source, target string) (int, int64, error) {
} }
// GetMimeType returns the content type // GetMimeType returns the content type
func (fs *MockOsFs) GetMimeType(name string) (string, error) { func (fs *MockOsFs) GetMimeType(_ string) (string, error) {
if fs.err != nil { if fs.err != nil {
return "", fs.err return "", fs.err
} }

View file

@ -4874,6 +4874,7 @@ components:
- 10 - 10
- 11 - 11
- 12 - 12
- 13
description: | description: |
Supported event action types: Supported event action types:
* `1` - HTTP * `1` - HTTP
@ -4888,6 +4889,7 @@ components:
* `10` - Metadata check * `10` - Metadata check
* `11` - Password expiration check * `11` - Password expiration check
* `12` - User expiration check * `12` - User expiration check
* `13` - Identity Provider account check
FilesystemActionTypes: FilesystemActionTypes:
type: integer type: integer
enum: enum:
@ -4910,6 +4912,7 @@ components:
- 4 - 4
- 5 - 5
- 6 - 6
- 7
description: | description: |
Supported event trigger types: Supported event trigger types:
* `1` - Filesystem event * `1` - Filesystem event
@ -4918,6 +4921,7 @@ components:
* `4` - IP blocked * `4` - IP blocked
* `5` - Certificate renewal * `5` - Certificate renewal
* `6` - On demand, like schedule but executed on demand * `6` - On demand, like schedule but executed on demand
* `7` - Identity provider login
LoginMethods: LoginMethods:
type: string type: string
enum: enum:
@ -6870,6 +6874,24 @@ components:
threshold: threshold:
type: integer 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' 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: BaseEventActionOptions:
type: object type: object
properties: properties:
@ -6885,6 +6907,8 @@ components:
$ref: '#/components/schemas/EventActionFilesystemConfig' $ref: '#/components/schemas/EventActionFilesystemConfig'
pwd_expiration_config: pwd_expiration_config:
$ref: '#/components/schemas/EventActionPasswordExpiration' $ref: '#/components/schemas/EventActionPasswordExpiration'
idp_config:
$ref: '#/components/schemas/EventActionIDPAccountCheck'
BaseEventAction: BaseEventAction:
type: object type: object
properties: properties:
@ -7026,6 +7050,8 @@ components:
- pre-upload - pre-upload
- pre-download - pre-download
- pre-delete - pre-delete
- first-upload
- first-download
provider_events: provider_events:
type: array type: array
items: items:
@ -7038,6 +7064,17 @@ components:
type: array type: array
items: items:
$ref: '#/components/schemas/Schedule' $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: options:
$ref: '#/components/schemas/ConditionOptions' $ref: '#/components/schemas/ConditionOptions'
BaseEventRule: BaseEventRule:

View file

@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
NFPM_VERSION=2.26.0 NFPM_VERSION=2.27.1
NFPM_ARCH=${NFPM_ARCH:-amd64} NFPM_ARCH=${NFPM_ARCH:-amd64}
if [ -z ${SFTPGO_VERSION} ] if [ -z ${SFTPGO_VERSION} ]
then then

View file

@ -88,6 +88,38 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
</div> </div>
</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"> <div class="form-group row action-type action-http">
<label for="idHTTPEndpoint" class="col-sm-2 col-form-label">Endpoint</label> <label for="idHTTPEndpoint" class="col-sm-2 col-form-label">Endpoint</label>
<div class="col-sm-10"> <div class="col-sm-10">
@ -781,6 +813,9 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
<p> <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. <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>
<p>
<span class="shortcut"><b>{{`{{IDPField<fieldname>}}`}}</b></span> => Identity Provider custom fields containing a string.
</p>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button class="btn btn-primary" type="button" data-dismiss="modal">OK</button> <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': case '11':
$('.action-pwd-expiration').show(); $('.action-pwd-expiration').show();
break; break;
case '13':
$('.action-idp').show();
break;
} }
} }

View file

@ -98,6 +98,17 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
</div> </div>
</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 bg-light mb-3 trigger trigger-schedule">
<div class="card-header"> <div class="card-header">
<b>Schedules</b> <b>Schedules</b>
@ -196,7 +207,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
</div> </div>
</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"> <div class="card-header">
<b>Name filters</b> <b>Name filters</b>
</div> </div>
@ -438,7 +449,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
<b>Actions</b> <b>Actions</b>
</div> </div>
<div class="card-body"> <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="form-group row">
<div class="col-md-12 form_field_action_outer"> <div class="col-md-12 form_field_action_outer">
{{range $idx, $val := .Rule.Actions}} {{range $idx, $val := .Rule.Actions}}
@ -727,6 +738,9 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
case '6': case '6':
$('.trigger-on-demand').show(); $('.trigger-on-demand').show();
break; break;
case '7':
$('.trigger-idp').show();
break;
default: default:
console.log(`unsupported event trigger type: ${val}`); console.log(`unsupported event trigger type: ${val}`);
} }

View file

@ -10,16 +10,16 @@ require (
require ( require (
github.com/fatih/color v1.15.0 // indirect github.com/fatih/color v1.15.0 // indirect
github.com/golang/protobuf v1.5.3 // 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/hashicorp/yamux v0.1.1 // indirect
github.com/mattn/go-colorable v0.1.13 // 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/mitchellh/go-testing-interface v1.14.1 // indirect
github.com/oklog/run v1.1.0 // indirect github.com/oklog/run v1.1.0 // indirect
golang.org/x/net v0.8.0 // indirect golang.org/x/net v0.8.0 // indirect
golang.org/x/sys v0.6.0 // indirect golang.org/x/sys v0.6.0 // indirect
golang.org/x/text v0.8.0 // indirect golang.org/x/text v0.8.0 // indirect
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 // indirect google.golang.org/genproto v0.0.0-20230320184635-7606e756e683 // indirect
google.golang.org/grpc v1.53.0 // indirect google.golang.org/grpc v1.54.0 // indirect
google.golang.org/protobuf v1.29.1 // indirect google.golang.org/protobuf v1.30.0 // indirect
) )

View file

@ -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/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.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 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.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c=
github.com/hashicorp/go-hclog v1.4.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= 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 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/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= 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.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.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.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.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 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 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= 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= 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 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 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= 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-20230320184635-7606e756e683 h1:khxVcsk/FhnzxMKOyD+TDGwjbEOpcPuIpmafPGFmhMA=
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= google.golang.org/genproto v0.0.0-20230320184635-7606e756e683/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s=
google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc= google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag=
google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= 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-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.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.29.1 h1:7QBf+IK2gx70Ap/hDsOmam3GE0v9HicjfEdAxE62UoM= google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 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/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 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View file

@ -10,16 +10,16 @@ require (
require ( require (
github.com/fatih/color v1.15.0 // indirect github.com/fatih/color v1.15.0 // indirect
github.com/golang/protobuf v1.5.3 // 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/hashicorp/yamux v0.1.1 // indirect
github.com/mattn/go-colorable v0.1.13 // 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/mitchellh/go-testing-interface v1.14.1 // indirect
github.com/oklog/run v1.1.0 // indirect github.com/oklog/run v1.1.0 // indirect
golang.org/x/net v0.8.0 // indirect golang.org/x/net v0.8.0 // indirect
golang.org/x/sys v0.6.0 // indirect golang.org/x/sys v0.6.0 // indirect
golang.org/x/text v0.8.0 // indirect golang.org/x/text v0.8.0 // indirect
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 // indirect google.golang.org/genproto v0.0.0-20230320184635-7606e756e683 // indirect
google.golang.org/grpc v1.53.0 // indirect google.golang.org/grpc v1.54.0 // indirect
google.golang.org/protobuf v1.29.1 // indirect google.golang.org/protobuf v1.30.0 // indirect
) )

View file

@ -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/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.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 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.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c=
github.com/hashicorp/go-hclog v1.4.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= 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 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/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= 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.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.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.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.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 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 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= 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= 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 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 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= 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-20230320184635-7606e756e683 h1:khxVcsk/FhnzxMKOyD+TDGwjbEOpcPuIpmafPGFmhMA=
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= google.golang.org/genproto v0.0.0-20230320184635-7606e756e683/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s=
google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc= google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag=
google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= 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-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.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.29.1 h1:7QBf+IK2gx70Ap/hDsOmam3GE0v9HicjfEdAxE62UoM= google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 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/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 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=