WebAdmin: allow to search and export event logs

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
Nicola Murino 2022-12-07 18:47:38 +01:00
parent 91e4a54385
commit 75d911f29e
No known key found for this signature in database
GPG key ID: 935D2952DEC4EECF
22 changed files with 1438 additions and 94 deletions

30
go.mod
View file

@ -24,17 +24,17 @@ require (
github.com/fclairamb/ftpserverlib v0.20.1-0.20221012093027-95be4ae0c9a6
github.com/fclairamb/go-log v0.4.1
github.com/go-acme/lego/v4 v4.9.1
github.com/go-chi/chi/v5 v5.0.8-0.20221018120124-e5529d9db4d3
github.com/go-chi/chi/v5 v5.0.8
github.com/go-chi/jwtauth/v5 v5.1.0
github.com/go-chi/render v1.0.2
github.com/go-sql-driver/mysql v1.7.0
github.com/golang/mock v1.6.0
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/google/uuid v1.3.0
github.com/hashicorp/go-hclog v1.3.1
github.com/hashicorp/go-hclog v1.4.0
github.com/hashicorp/go-plugin v1.4.6
github.com/hashicorp/go-retryablehttp v0.7.1
github.com/jackc/pgx/v5 v5.1.1
github.com/jackc/pgx/v5 v5.2.0
github.com/jlaffaye/ftp v0.0.0-20201112195030-9aae4d151126
github.com/klauspost/compress v1.15.12
github.com/lestrrat-go/jwx/v2 v2.0.8
@ -51,7 +51,7 @@ require (
github.com/rs/cors v1.8.3-0.20220619195839-da52b0701de5
github.com/rs/xid v1.4.0
github.com/rs/zerolog v1.28.0
github.com/sftpgo/sdk v0.1.3-0.20221203095324-2feef3600930
github.com/sftpgo/sdk v0.1.3-0.20221205110949-c15308d4236e
github.com/shirou/gopsutil/v3 v3.22.11
github.com/spf13/afero v1.9.3
github.com/spf13/cobra v1.6.1
@ -67,19 +67,19 @@ require (
go.uber.org/automaxprocs v1.5.1
gocloud.dev v0.27.0
golang.org/x/crypto v0.3.0
golang.org/x/net v0.2.0
golang.org/x/oauth2 v0.2.0
golang.org/x/sys v0.2.0
golang.org/x/time v0.2.0
google.golang.org/api v0.103.0
golang.org/x/net v0.4.0
golang.org/x/oauth2 v0.3.0
golang.org/x/sys v0.3.0
golang.org/x/time v0.3.0
google.golang.org/api v0.104.0
gopkg.in/natefinch/lumberjack.v2 v2.0.0
)
require (
cloud.google.com/go v0.107.0 // indirect
cloud.google.com/go/compute v1.13.0 // indirect
cloud.google.com/go/compute v1.14.0 // indirect
cloud.google.com/go/compute/metadata v0.2.2 // indirect
cloud.google.com/go/iam v0.7.0 // indirect
cloud.google.com/go/iam v0.8.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.1 // indirect
github.com/ajg/form v1.5.1 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 // indirect
@ -119,7 +119,7 @@ require (
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/klauspost/cpuid/v2 v2.2.1 // indirect
github.com/klauspost/cpuid/v2 v2.2.2 // indirect
github.com/kr/fs v0.1.0 // indirect
github.com/lestrrat-go/blackmagic v1.0.1 // indirect
github.com/lestrrat-go/httpcc v1.0.1 // indirect
@ -154,11 +154,11 @@ require (
github.com/yusufpapurcu/wmi v1.2.2 // indirect
go.opencensus.io v0.24.0 // indirect
golang.org/x/mod v0.7.0 // indirect
golang.org/x/text v0.4.0 // indirect
golang.org/x/tools v0.3.0 // indirect
golang.org/x/text v0.5.0 // indirect
golang.org/x/tools v0.4.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd // indirect
google.golang.org/genproto v0.0.0-20221207170731-23e4bf6bdc37 // indirect
google.golang.org/grpc v1.51.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect

58
go.sum
View file

@ -50,8 +50,8 @@ cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6m
cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s=
cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU=
cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U=
cloud.google.com/go/compute v1.13.0 h1:AYrLkB8NPdDRslNp4Jxmzrhdr03fUAIDbiGFjLWowoU=
cloud.google.com/go/compute v1.13.0/go.mod h1:5aPTS0cUNMIc1CE546K+Th6weJUNQErARyZtRXDJ8GE=
cloud.google.com/go/compute v1.14.0 h1:hfm2+FfxVmnRlh6LpB7cg1ZNU+5edAHmW679JePztk0=
cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo=
cloud.google.com/go/compute/metadata v0.2.2 h1:aWKAjYaBaOSrpKl57+jnS/3fJRQnxL7TvR/u1VVbt6k=
cloud.google.com/go/compute/metadata v0.2.2/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
@ -60,8 +60,8 @@ cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqCl
cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY=
cloud.google.com/go/iam v0.1.0/go.mod h1:vcUNEa0pEm0qRVpmWepWaFMIAI8/hjB9mO8rNCJtF6c=
cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY=
cloud.google.com/go/iam v0.7.0 h1:k4MuwOsS7zGJJ+QfZ5vBK8SgHBAvYN/23BWsiihJ1vs=
cloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQEYOeg=
cloud.google.com/go/iam v0.8.0 h1:E2osAkZzxI/+8pZcxVLcDtAQx/u+hZXVryUaYQ5O0Kk=
cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE=
cloud.google.com/go/kms v1.4.0/go.mod h1:fajBHndQ+6ubNw6Ss2sSd+SWvjL26RNo/dr7uxsnnOA=
cloud.google.com/go/kms v1.6.0 h1:OWRZzrPmOZUzurjI2FBGtgY2mB1WaJkqhw6oIwSj0Yg=
cloud.google.com/go/longrunning v0.3.0 h1:NjljC+FYPV3uh5/OwWT6pVU+doBqMg2x/rZlE+CamDs=
@ -609,8 +609,8 @@ github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwv
github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U=
github.com/go-acme/lego/v4 v4.9.1 h1:n9Z5MQwANeGSQKlVE3bEh9SDvAySK9oVYOKCGCESqQE=
github.com/go-acme/lego/v4 v4.9.1/go.mod h1:g3JRUyWS3L/VObpp4bCxzJftKyf/Wba8QrSSnoiqjg4=
github.com/go-chi/chi/v5 v5.0.8-0.20221018120124-e5529d9db4d3 h1:qzwVVqrbdP93ZaSHy0yWQRYnig+t+j1OxnVtEs8SFuQ=
github.com/go-chi/chi/v5 v5.0.8-0.20221018120124-e5529d9db4d3/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0=
github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/jwtauth/v5 v5.1.0 h1:wJyf2YZ/ohPvNJBwPOzZaQbyzwgMZZceE1m8FOzXLeA=
github.com/go-chi/jwtauth/v5 v5.1.0/go.mod h1:MA93hc1au3tAQwCKry+fI4LqJ5MIVN4XSsglOo+lSc8=
github.com/go-chi/render v1.0.2 h1:4ER/udB0+fMWB2Jlf15RV3F4A2FDuYi/9f+lFttR/Lg=
@ -914,8 +914,8 @@ github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/S
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-hclog v0.12.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-hclog v1.3.1 h1:vDwF1DFNZhntP4DAjuTpOw3uEgMUpXh1pB5fW9DqHpo=
github.com/hashicorp/go-hclog v1.3.1/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
github.com/hashicorp/go-hclog v1.4.0 h1:ctuWFGrhFha8BnnzxqeRGidlEcQkDyL5u8J8t5eA11I=
github.com/hashicorp/go-hclog v1.4.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
github.com/hashicorp/go-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-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
@ -1014,8 +1014,8 @@ github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQ
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
github.com/jackc/pgx/v4 v4.16.0/go.mod h1:N0A9sFdWzkw/Jy1lwoiB64F2+ugFZi987zRxcPez/wI=
github.com/jackc/pgx/v4 v4.16.1/go.mod h1:SIhx0D5hoADaiXZVyv+3gSm3LCIIINTVO0PficsvWGQ=
github.com/jackc/pgx/v5 v5.1.1 h1:pZD79K1SYv8wc2HmCQA6VdmRQi7/OtCfv9bM3WAXUYA=
github.com/jackc/pgx/v5 v5.1.1/go.mod h1:Ptn7zmohNsWEsdxRawMzk3gaKma2obW+NWTnKa0S4nk=
github.com/jackc/pgx/v5 v5.2.0 h1:NdPpngX0Y6z6XDFKqmFQaE+bCtkqzvQIOt1wvBlAqs8=
github.com/jackc/pgx/v5 v5.2.0/go.mod h1:Ptn7zmohNsWEsdxRawMzk3gaKma2obW+NWTnKa0S4nk=
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
@ -1064,8 +1064,9 @@ github.com/klauspost/compress v1.15.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47e
github.com/klauspost/compress v1.15.12 h1:YClS/PImqYbn+UILDnqxQCZ3RehC9N318SU3kElDUEM=
github.com/klauspost/compress v1.15.12/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.1 h1:U33DW0aiEj633gHYw3LoDNfkDiYnE5Q8M/TKJn2f2jI=
github.com/klauspost/cpuid/v2 v2.2.1/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
github.com/klauspost/cpuid/v2 v2.2.2 h1:xPMwiykqNK9VK0NYC3+jTMYv9I6Vl3YdjZgPZKG3zO0=
github.com/klauspost/cpuid/v2 v2.2.2/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
github.com/kolo/xmlrpc v0.0.0-20201022064351-38db28db192b/go.mod h1:pcaDhQK0/NJZEvtCO0qQPPropqV0sJOJ6YW7X+9kRwM=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@ -1449,8 +1450,8 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
github.com/secsy/goftp v0.0.0-20200609142545-aa2de14babf4 h1:PT+ElG/UUFMfqy5HrxJxNzj3QBOf7dZwupeVC+mG1Lo=
github.com/sftpgo/sdk v0.1.3-0.20221203095324-2feef3600930 h1:znJ52fQBSAQhacaQvZAfkpTioqpcutDREM/H8NttKzU=
github.com/sftpgo/sdk v0.1.3-0.20221203095324-2feef3600930/go.mod h1:S2S/Q9fgUpXmL11YoCCt0hyCkEwH1LzQM/6QVsbUCFg=
github.com/sftpgo/sdk v0.1.3-0.20221205110949-c15308d4236e h1:F3G/BReUSU8TX6Kmk0moQgQAk9Ouiv2I+pg//o1IR6U=
github.com/sftpgo/sdk v0.1.3-0.20221205110949-c15308d4236e/go.mod h1:S2S/Q9fgUpXmL11YoCCt0hyCkEwH1LzQM/6QVsbUCFg=
github.com/shirou/gopsutil/v3 v3.22.11 h1:kxsPKS+Eeo+VnEQ2XCaGJepeP6KY53QoRTETx3+1ndM=
github.com/shirou/gopsutil/v3 v3.22.11/go.mod h1:xl0EeL4vXJ+hQMAGN8B9VFpxukEMA0XdevQOe5MZ1oY=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
@ -1814,8 +1815,9 @@ golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220802222814-0bcc04d9c69b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU=
golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -1842,8 +1844,8 @@ golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7Lm
golang.org/x/oauth2 v0.0.0-20220628200809-02e64fa58f26/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=
golang.org/x/oauth2 v0.0.0-20220722155238-128564f6959c/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
golang.org/x/oauth2 v0.2.0 h1:GtQkldQ9m7yvzCL1V+LrYow3Khe0eJH0w7RbX/VbaIU=
golang.org/x/oauth2 v0.2.0/go.mod h1:Cwn6afJ8jrQwYMxQDTpISoXmXW9I6qF6vDeuuoX3Ibs=
golang.org/x/oauth2 v0.3.0 h1:6l90koy8/LaBLmLu8jpHeHexzMwEita0zFfYlggy2F8=
golang.org/x/oauth2 v0.3.0/go.mod h1:rQrIauxkUhJ6CuwEXwymO2/eh4xz2ZWF1nBkcxS+tGk=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -2005,14 +2007,15 @@ golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -2022,8 +2025,9 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@ -2036,8 +2040,8 @@ golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxb
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20220609170525-579cf78fd858/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.2.0 h1:52I/1L54xyEQAYdtcSuxtiT84KGYTBGXwayxmIpNJhE=
golang.org/x/time v0.2.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@ -2122,8 +2126,8 @@ golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.3.0 h1:SrNbZl6ECOS1qFzgTdQfWXZM9XBkiA6tkFrH9YSTPHM=
golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
golang.org/x/tools v0.4.0 h1:7mTAgkunk3fr4GAloyyCasadO6h9zSsQZbwvcaIciV4=
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -2183,8 +2187,8 @@ google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6F
google.golang.org/api v0.86.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw=
google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw=
google.golang.org/api v0.91.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw=
google.golang.org/api v0.103.0 h1:9yuVqlu2JCvcLg9p8S3fcFLZij8EPSyvODIY1rkMizQ=
google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0=
google.golang.org/api v0.104.0 h1:KBfmLRqdZEbwQleFlSLnzpQJwhjpmNOk4cKQIBDZ9mg=
google.golang.org/api v0.104.0/go.mod h1:JCspTXJbBxa5ySXw4UgUqVer7DfVxbvc/CTUFqAED5U=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@ -2295,8 +2299,8 @@ google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljW
google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
google.golang.org/genproto v0.0.0-20220802133213-ce4fa296bf78/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc=
google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd h1:OjndDrsik+Gt+e6fs45z9AxiewiKyLKYpA45W5Kpkks=
google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE=
google.golang.org/genproto v0.0.0-20221207170731-23e4bf6bdc37 h1:jmIfw8+gSvXcZSgaFAGyInDXeWzUhvYH57G/5GKMn70=
google.golang.org/genproto v0.0.0-20221207170731-23e4bf6bdc37/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=

View file

@ -15,9 +15,13 @@
package httpd
import (
"encoding/csv"
"encoding/json"
"fmt"
"net/http"
"strconv"
"strings"
"time"
"github.com/sftpgo/sdk/plugin/eventsearcher"
@ -88,7 +92,6 @@ func getFsSearchParamsFromRequest(r *http.Request) (eventsearcher.FsEventSearch,
}
s.FsProvider = val
}
s.IP = r.URL.Query().Get("ip")
s.SSHCmd = r.URL.Query().Get("ssh_cmd")
s.Bucket = r.URL.Query().Get("bucket")
s.Endpoint = r.URL.Query().Get("endpoint")
@ -132,6 +135,14 @@ func searchFsEvents(w http.ResponseWriter, r *http.Request) {
}
filters.Role = getRoleFilterForEventSearch(r, claims.Role)
if getBoolQueryParam(r, "csv_export") {
filters.Limit = 100
if err := exportFsEvents(w, &filters); err != nil {
panic(http.ErrAbortHandler)
}
return
}
data, _, _, err := plugin.Handler.SearchFsEvents(&filters)
if err != nil {
sendAPIResponse(w, r, err, "", getRespStatus(err))
@ -150,13 +161,21 @@ func searchProviderEvents(w http.ResponseWriter, r *http.Request) {
return
}
filters, err := getProviderSearchParamsFromRequest(r)
if err != nil {
var filters eventsearcher.ProviderEventSearch
if filters, err = getProviderSearchParamsFromRequest(r); err != nil {
sendAPIResponse(w, r, err, "", getRespStatus(err))
return
}
filters.Role = getRoleFilterForEventSearch(r, claims.Role)
if getBoolQueryParam(r, "csv_export") {
filters.Limit = 100
if err := exportProviderEvents(w, &filters); err != nil {
panic(http.ErrAbortHandler)
}
return
}
data, _, _, err := plugin.Handler.SearchProviderEvents(&filters)
if err != nil {
sendAPIResponse(w, r, err, "", getRespStatus(err))
@ -167,9 +186,159 @@ func searchProviderEvents(w http.ResponseWriter, r *http.Request) {
w.Write(data) //nolint:errcheck
}
func exportFsEvents(w http.ResponseWriter, filters *eventsearcher.FsEventSearch) error {
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=fslogs-%s.csv", time.Now().Format("2006-01-02T15-04-05")))
w.Header().Set("Content-Type", "text/csv")
w.Header().Set("Accept-Ranges", "none")
w.WriteHeader(http.StatusOK)
csvWriter := csv.NewWriter(w)
ev := fsEvent{}
err := csvWriter.Write(ev.getCSVHeader())
if err != nil {
return err
}
results := make([]fsEvent, 0, filters.Limit)
for {
data, _, _, err := plugin.Handler.SearchFsEvents(filters)
if err != nil {
return err
}
if err := json.Unmarshal(data, &results); err != nil {
return err
}
for _, event := range results {
if err := csvWriter.Write(event.getCSVData()); err != nil {
return err
}
}
if len(results) == 0 || len(results) < filters.Limit {
break
}
filters.StartTimestamp = results[len(results)-1].Timestamp
filters.ExcludeIDs = []string{results[len(results)-1].ID}
results = nil
}
csvWriter.Flush()
return csvWriter.Error()
}
func exportProviderEvents(w http.ResponseWriter, filters *eventsearcher.ProviderEventSearch) error {
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=providerlogs-%s.csv", time.Now().Format("2006-01-02T15-04-05")))
w.Header().Set("Content-Type", "text/csv")
w.Header().Set("Accept-Ranges", "none")
w.WriteHeader(http.StatusOK)
ev := providerEvent{}
csvWriter := csv.NewWriter(w)
err := csvWriter.Write(ev.getCSVHeader())
if err != nil {
return err
}
results := make([]providerEvent, 0, filters.Limit)
for {
data, _, _, err := plugin.Handler.SearchProviderEvents(filters)
if err != nil {
return err
}
if err := json.Unmarshal(data, &results); err != nil {
return err
}
for _, event := range results {
if err := csvWriter.Write(event.getCSVData()); err != nil {
return err
}
}
if len(results) == 0 || len(results) < filters.Limit {
break
}
filters.StartTimestamp = results[len(results)-1].Timestamp
filters.ExcludeIDs = []string{results[len(results)-1].ID}
results = nil
}
csvWriter.Flush()
return csvWriter.Error()
}
func getRoleFilterForEventSearch(r *http.Request, defaultValue string) string {
if defaultValue != "" {
return defaultValue
}
return r.URL.Query().Get("role")
}
type fsEvent struct {
ID string `json:"id"`
Timestamp int64 `json:"timestamp"`
Action string `json:"action"`
Username string `json:"username"`
FsPath string `json:"fs_path"`
FsTargetPath string `json:"fs_target_path,omitempty"`
VirtualPath string `json:"virtual_path"`
VirtualTargetPath string `json:"virtual_target_path,omitempty"`
SSHCmd string `json:"ssh_cmd,omitempty"`
FileSize int64 `json:"file_size,omitempty"`
Status int `json:"status"`
Protocol string `json:"protocol"`
IP string `json:"ip,omitempty"`
SessionID string `json:"session_id"`
FsProvider int `json:"fs_provider"`
Bucket string `json:"bucket,omitempty"`
Endpoint string `json:"endpoint,omitempty"`
OpenFlags int `json:"open_flags,omitempty"`
Role string `json:"role,omitempty"`
InstanceID string `json:"instance_id,omitempty"`
}
func (e *fsEvent) getCSVHeader() []string {
return []string{"Time", "Action", "Path", "Size", "Status", "User", "Protocol",
"IP", "SSH command"}
}
func (e *fsEvent) getCSVData() []string {
timestamp := time.Unix(0, e.Timestamp).UTC()
var pathInfo strings.Builder
pathInfo.Write([]byte(e.VirtualPath))
if e.VirtualTargetPath != "" {
pathInfo.WriteString(" => ")
pathInfo.WriteString(e.VirtualTargetPath)
}
var status string
switch e.Status {
case 1:
status = "OK"
case 2:
status = "KO"
case 3:
status = "Quota exceeded"
}
var fileSize string
if e.FileSize > 0 {
fileSize = util.ByteCountIEC(e.FileSize)
}
return []string{timestamp.Format(time.RFC3339), e.Action, pathInfo.String(),
fileSize, status, e.Username, e.Protocol, e.IP, e.SSHCmd}
}
type providerEvent struct {
ID string `json:"id"`
Timestamp int64 `json:"timestamp"`
Action string `json:"action"`
Username string `json:"username"`
IP string `json:"ip,omitempty"`
ObjectType string `json:"object_type"`
ObjectName string `json:"object_name"`
ObjectData []byte `json:"object_data"`
Role string `json:"role,omitempty"`
InstanceID string `json:"instance_id,omitempty"`
}
func (e *providerEvent) getCSVHeader() []string {
return []string{"Time", "Action", "Object Type", "Object Name", "User", "IP"}
}
func (e *providerEvent) getCSVData() []string {
timestamp := time.Unix(0, e.Timestamp).UTC()
return []string{timestamp.Format(time.RFC3339), e.Action, e.ObjectType, e.ObjectName,
e.Username, e.IP}
}

View file

@ -139,6 +139,9 @@ const (
webTemplateFolderDefault = "/web/admin/template/folder"
webDefenderPathDefault = "/web/admin/defender"
webDefenderHostsPathDefault = "/web/admin/defender/hosts"
webEventsPathDefault = "/web/admin/events"
webEventsFsSearchPathDefault = "/web/admin/events/fs"
webEventsProviderSearchPathDefault = "/web/admin/events/provider"
webClientLoginPathDefault = "/web/client/login"
webClientOIDCLoginPathDefault = "/web/client/oidclogin"
webClientTwoFactorPathDefault = "/web/client/twofactor"
@ -226,6 +229,9 @@ var (
webTemplateUser string
webTemplateFolder string
webDefenderPath string
webEventsPath string
webEventsFsSearchPath string
webEventsProviderSearchPath string
webDefenderHostsPath string
webClientLoginPath string
webClientOIDCLoginPath string
@ -1025,6 +1031,9 @@ func updateWebAdminURLs(baseURL string) {
webTemplateFolder = path.Join(baseURL, webTemplateFolderDefault)
webDefenderHostsPath = path.Join(baseURL, webDefenderHostsPathDefault)
webDefenderPath = path.Join(baseURL, webDefenderPathDefault)
webEventsPath = path.Join(baseURL, webEventsPathDefault)
webEventsFsSearchPath = path.Join(baseURL, webEventsFsSearchPathDefault)
webEventsProviderSearchPath = path.Join(baseURL, webEventsProviderSearchPathDefault)
webStaticFilesPath = path.Join(baseURL, webStaticFilesPathDefault)
webOpenAPIPath = path.Join(baseURL, webOpenAPIPathDefault)
}

View file

@ -162,6 +162,7 @@ const (
webAdminEventActionPath = "/web/admin/eventaction"
webAdminRolesPath = "/web/admin/roles"
webAdminRolePath = "/web/admin/role"
webEventsPath = "/web/admin/events"
webBasePathClient = "/web/client"
webClientLoginPath = "/web/client/login"
webClientFilesPath = "/web/client/files"
@ -8791,6 +8792,8 @@ func TestWebUserTwoFactorLogin(t *testing.T) {
func TestSearchEvents(t *testing.T) {
token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
assert.NoError(t, err)
webToken, err := getJWTWebTokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
assert.NoError(t, err)
req, err := http.NewRequest(http.MethodGet, fsEventsPath+"?limit=10&order=ASC&fs_provider=0", nil)
assert.NoError(t, err)
@ -8817,13 +8820,32 @@ func TestSearchEvents(t *testing.T) {
err = json.Unmarshal(rr.Body.Bytes(), &events)
assert.NoError(t, err)
assert.Len(t, events, 1)
// CSV export
req, err = http.NewRequest(http.MethodGet, fsEventsPath+"?limit=10&order=ASC&csv_export=true", nil)
assert.NoError(t, err)
setBearerForReq(req, token)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr)
assert.Equal(t, "text/csv", rr.Header().Get("Content-Type"))
// the test eventsearcher plugin returns error if start_timestamp < 0
req, err = http.NewRequest(http.MethodGet, fsEventsPath+"?start_timestamp=-1&end_timestamp=123456&statuses=1,2", nil)
assert.NoError(t, err)
setBearerForReq(req, token)
rr = executeRequest(req)
checkResponseCode(t, http.StatusInternalServerError, rr)
// CSV export with error
exportFunc := func() {
defer func() {
rcv := recover()
assert.Equal(t, http.ErrAbortHandler, rcv)
}()
req, err = http.NewRequest(http.MethodGet, fsEventsPath+"?start_timestamp=-1&csv_export=true", nil)
assert.NoError(t, err)
setBearerForReq(req, token)
rr = executeRequest(req)
}
exportFunc()
req, err = http.NewRequest(http.MethodGet, fsEventsPath+"?limit=a", nil)
assert.NoError(t, err)
@ -8847,6 +8869,13 @@ func TestSearchEvents(t *testing.T) {
assert.True(t, ok, field)
}
}
// CSV export
req, err = http.NewRequest(http.MethodGet, providerEventsPath+"?csv_export=true", nil)
assert.NoError(t, err)
setBearerForReq(req, token)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr)
assert.Equal(t, "text/csv", rr.Header().Get("Content-Type"))
// the test eventsearcher plugin returns error if start_timestamp < 0
req, err = http.NewRequest(http.MethodGet, providerEventsPath+"?start_timestamp=-1", nil)
@ -8854,6 +8883,19 @@ func TestSearchEvents(t *testing.T) {
setBearerForReq(req, token)
rr = executeRequest(req)
checkResponseCode(t, http.StatusInternalServerError, rr)
// CSV export with error
exportFunc = func() {
defer func() {
rcv := recover()
assert.Equal(t, http.ErrAbortHandler, rcv)
}()
req, err = http.NewRequest(http.MethodGet, providerEventsPath+"?start_timestamp=-1&csv_export=true", nil)
assert.NoError(t, err)
setBearerForReq(req, token)
rr = executeRequest(req)
}
exportFunc()
req, err = http.NewRequest(http.MethodGet, providerEventsPath+"?limit=2000", nil)
assert.NoError(t, err)
@ -8890,6 +8932,12 @@ func TestSearchEvents(t *testing.T) {
setBearerForReq(req, token)
rr = executeRequest(req)
checkResponseCode(t, http.StatusBadRequest, rr)
req, err = http.NewRequest(http.MethodGet, webEventsPath, nil)
assert.NoError(t, err)
setJWTCookieForReq(req, webToken)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr)
}
func TestMFAErrors(t *testing.T) {

View file

@ -2911,6 +2911,20 @@ func TestEventRoleFilter(t *testing.T) {
assert.Equal(t, "role1", role)
}
func TestEventsCSV(t *testing.T) {
e := fsEvent{
Status: 1,
}
data := e.getCSVData()
assert.Equal(t, "OK", data[4])
e.Status = 2
data = e.getCSVData()
assert.Equal(t, "KO", data[4])
e.Status = 3
data = e.getCSVData()
assert.Equal(t, "Quota exceeded", data[4])
}
func isSharedProviderSupported() bool {
// SQLite shares the implementation with other SQL-based provider but it makes no sense
// to use it outside test cases

View file

@ -1662,6 +1662,12 @@ func (s *httpdServer) setupWebAdminRoutes() {
s.handleWebUpdateRolePost)
router.With(s.checkPerm(dataprovider.PermAdminManageRoles), verifyCSRFHeader).
Delete(webAdminRolePath+"/{name}", deleteRole)
router.With(s.checkPerm(dataprovider.PermAdminViewEvents), s.refreshCookie).Get(webEventsPath,
s.handleWebGetEvents)
router.With(s.checkPerm(dataprovider.PermAdminViewEvents), compressor.Handler, s.refreshCookie).
Get(webEventsFsSearchPath, searchFsEvents)
router.With(s.checkPerm(dataprovider.PermAdminViewEvents), compressor.Handler, s.refreshCookie).
Get(webEventsProviderSearchPath, searchProviderEvents)
})
}
}

View file

@ -36,6 +36,7 @@ import (
"github.com/drakkan/sftpgo/v2/internal/dataprovider"
"github.com/drakkan/sftpgo/v2/internal/kms"
"github.com/drakkan/sftpgo/v2/internal/mfa"
"github.com/drakkan/sftpgo/v2/internal/plugin"
"github.com/drakkan/sftpgo/v2/internal/smtp"
"github.com/drakkan/sftpgo/v2/internal/util"
"github.com/drakkan/sftpgo/v2/internal/version"
@ -86,6 +87,7 @@ const (
templateEventAction = "eventaction.html"
templateRoles = "roles.html"
templateRole = "role.html"
templateEvents = "events.html"
templateMessage = "message.html"
templateStatus = "status.html"
templateLogin = "login.html"
@ -108,6 +110,7 @@ const (
pageChangePwdTitle = "Change password"
pageMaintenanceTitle = "Maintenance"
pageDefenderTitle = "Defender"
pageEventsTitle = "Logs"
pageForgotPwdTitle = "SFTPGo Admin - Forgot password"
pageResetPwdTitle = "SFTPGo Admin - Reset password"
pageSetupTitle = "Create first admin user"
@ -135,6 +138,7 @@ type basePage struct {
FolderURL string
FolderTemplateURL string
DefenderURL string
EventsURL string
LogoutURL string
ProfileURL string
ChangePwdURL string
@ -160,10 +164,12 @@ type basePage struct {
StatusTitle string
MaintenanceTitle string
DefenderTitle string
EventsTitle string
Version string
CSRFToken string
IsEventManagerPage bool
HasDefender bool
HasSearcher bool
HasExternalLogin bool
LoggedAdmin *dataprovider.Admin
Branding UIBranding
@ -349,6 +355,12 @@ type eventRulePage struct {
IsShared bool
}
type eventsPage struct {
basePage
FsEventsSearchURL string
ProviderEventsSearchURL string
}
type messagePage struct {
basePage
Error string
@ -505,6 +517,11 @@ func loadAdminTemplates(templatesPath string) {
filepath.Join(templatesPath, templateAdminDir, templateBase),
filepath.Join(templatesPath, templateAdminDir, templateRole),
}
eventsPaths := []string{
filepath.Join(templatesPath, templateCommonDir, templateCommonCSS),
filepath.Join(templatesPath, templateAdminDir, templateBase),
filepath.Join(templatesPath, templateAdminDir, templateEvents),
}
fsBaseTpl := template.New("fsBaseTemplate").Funcs(template.FuncMap{
"ListFSProviders": func() []sdk.FilesystemProvider {
@ -543,6 +560,7 @@ func loadAdminTemplates(templatesPath string) {
resetPwdTmpl := util.LoadTemplate(nil, resetPwdPaths...)
rolesTmpl := util.LoadTemplate(nil, rolesPaths...)
roleTmpl := util.LoadTemplate(nil, rolePaths...)
eventsTmpl := util.LoadTemplate(nil, eventsPaths...)
adminTemplates[templateUsers] = usersTmpl
adminTemplates[templateUser] = userTmpl
@ -572,6 +590,7 @@ func loadAdminTemplates(templatesPath string) {
adminTemplates[templateResetPassword] = resetPwdTmpl
adminTemplates[templateRoles] = rolesTmpl
adminTemplates[templateRole] = roleTmpl
adminTemplates[templateEvents] = eventsTmpl
}
func isEventManagerResource(currentURL string) bool {
@ -609,6 +628,7 @@ func (s *httpdServer) getBasePageData(title, currentURL string, r *http.Request)
FolderURL: webFolderPath,
FolderTemplateURL: webTemplateFolder,
DefenderURL: webDefenderPath,
EventsURL: webEventsPath,
LogoutURL: webLogoutPath,
ProfileURL: webAdminProfilePath,
ChangePwdURL: webChangeAdminPwdPath,
@ -636,10 +656,12 @@ func (s *httpdServer) getBasePageData(title, currentURL string, r *http.Request)
StatusTitle: pageStatusTitle,
MaintenanceTitle: pageMaintenanceTitle,
DefenderTitle: pageDefenderTitle,
EventsTitle: pageEventsTitle,
Version: version.GetAsString(),
LoggedAdmin: getAdminFromToken(r),
IsEventManagerPage: isEventManagerResource(currentURL),
HasDefender: common.Config.DefenderConfig.Enabled,
HasSearcher: plugin.Handler.HasSearcher(),
HasExternalLogin: isLoggedInWithOIDC(r),
CSRFToken: csrfToken,
Branding: s.binding.Branding.WebAdmin,
@ -3636,3 +3658,14 @@ func (s *httpdServer) handleWebUpdateRolePost(w http.ResponseWriter, r *http.Req
}
http.Redirect(w, r, webAdminRolesPath, http.StatusSeeOther)
}
func (s *httpdServer) handleWebGetEvents(w http.ResponseWriter, r *http.Request) {
r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
data := eventsPage{
basePage: s.getBasePageData(pageEventsTitle, webEventsPath, r),
FsEventsSearchURL: webEventsFsSearchPath,
ProviderEventsSearchURL: webEventsProviderSearchPath,
}
renderAdminTemplate(w, templateEvents, data)
}

View file

@ -291,6 +291,11 @@ func (m *Manager) NotifyProviderEvent(event *notifier.ProviderEvent, object Rend
}
}
// HasSearcher returns true if an event searcher plugin is defined
func (m *Manager) HasSearcher() bool {
return m.hasSearcher
}
// SearchFsEvents returns the filesystem events matching the specified filters
func (m *Manager) SearchFsEvents(searchFilters *eventsearcher.FsEventSearch) ([]byte, []string, []string, error) {
if !m.hasSearcher {

View file

@ -2305,6 +2305,13 @@ paths:
type: string
description: 'User role. Empty or missing means omit this filter. Ignored if the admin has a role'
required: false
- in: query
name: csv_export
schema:
type: boolean
default: false
required: false
description: 'If enabled, events are exported as a CSV file'
- in: query
name: limit
schema:
@ -2313,7 +2320,7 @@ paths:
maximum: 1000
default: 100
required: false
description: 'The maximum number of items to return. Max value is 500, default is 100'
description: 'The maximum number of items to return. Max value is 1000, default is 100'
- in: query
name: order
required: false
@ -2333,6 +2340,9 @@ paths:
type: array
items:
$ref: '#/components/schemas/FsEvent'
text/csv:
schema:
type: string
'400':
$ref: '#/components/responses/BadRequest'
'401':
@ -2429,6 +2439,13 @@ paths:
type: string
description: 'Admin role. Empty or missing means omit this filter. Ignored if the admin has a role'
required: false
- in: query
name: csv_export
schema:
type: boolean
default: false
required: false
description: 'If enabled, events are exported as a CSV file'
- in: query
name: limit
schema:
@ -2437,7 +2454,7 @@ paths:
maximum: 1000
default: 100
required: false
description: 'The maximum number of items to return. Max value is 500, default is 100'
description: 'The maximum number of items to return. Max value is 1000, default is 100'
- in: query
name: order
required: false
@ -2457,6 +2474,9 @@ paths:
type: array
items:
$ref: '#/components/schemas/ProviderEvent'
text/csv:
schema:
type: string
'400':
$ref: '#/components/responses/BadRequest'
'401':
@ -4616,16 +4636,19 @@ components:
- HTTP
- HTTPShare
- DataRetention
- EventAction
- OIDC
description: |
Protocols:
* `SSH` - SSH commands
* `SFTP` - SFTP protocol
* `SCP` - SCP protocol
* `FTP` - plain FTP and FTPES/FTPS
* `DAV` - WebDAV
* `HTTP` - WebClient/REST API
* `HTTPShare` - the event is generated in a public share
* `DataRetention` - the event is generated by a data retention check
* `EventAction` - the event is generated by an EventManager action
* `OIDC` - OpenID Connect
WebClientOptions:
type: string

View file

@ -0,0 +1,410 @@
.daterangepicker {
position: absolute;
color: inherit;
background-color: #fff;
border-radius: 4px;
border: 1px solid #ddd;
width: 278px;
max-width: none;
padding: 0;
margin-top: 7px;
top: 100px;
left: 20px;
z-index: 3001;
display: none;
font-family: arial;
font-size: 15px;
line-height: 1em;
}
.daterangepicker:before, .daterangepicker:after {
position: absolute;
display: inline-block;
border-bottom-color: rgba(0, 0, 0, 0.2);
content: '';
}
.daterangepicker:before {
top: -7px;
border-right: 7px solid transparent;
border-left: 7px solid transparent;
border-bottom: 7px solid #ccc;
}
.daterangepicker:after {
top: -6px;
border-right: 6px solid transparent;
border-bottom: 6px solid #fff;
border-left: 6px solid transparent;
}
.daterangepicker.opensleft:before {
right: 9px;
}
.daterangepicker.opensleft:after {
right: 10px;
}
.daterangepicker.openscenter:before {
left: 0;
right: 0;
width: 0;
margin-left: auto;
margin-right: auto;
}
.daterangepicker.openscenter:after {
left: 0;
right: 0;
width: 0;
margin-left: auto;
margin-right: auto;
}
.daterangepicker.opensright:before {
left: 9px;
}
.daterangepicker.opensright:after {
left: 10px;
}
.daterangepicker.drop-up {
margin-top: -7px;
}
.daterangepicker.drop-up:before {
top: initial;
bottom: -7px;
border-bottom: initial;
border-top: 7px solid #ccc;
}
.daterangepicker.drop-up:after {
top: initial;
bottom: -6px;
border-bottom: initial;
border-top: 6px solid #fff;
}
.daterangepicker.single .daterangepicker .ranges, .daterangepicker.single .drp-calendar {
float: none;
}
.daterangepicker.single .drp-selected {
display: none;
}
.daterangepicker.show-calendar .drp-calendar {
display: block;
}
.daterangepicker.show-calendar .drp-buttons {
display: block;
}
.daterangepicker.auto-apply .drp-buttons {
display: none;
}
.daterangepicker .drp-calendar {
display: none;
max-width: 270px;
}
.daterangepicker .drp-calendar.left {
padding: 8px 0 8px 8px;
}
.daterangepicker .drp-calendar.right {
padding: 8px;
}
.daterangepicker .drp-calendar.single .calendar-table {
border: none;
}
.daterangepicker .calendar-table .next span, .daterangepicker .calendar-table .prev span {
color: #fff;
border: solid black;
border-width: 0 2px 2px 0;
border-radius: 0;
display: inline-block;
padding: 3px;
}
.daterangepicker .calendar-table .next span {
transform: rotate(-45deg);
-webkit-transform: rotate(-45deg);
}
.daterangepicker .calendar-table .prev span {
transform: rotate(135deg);
-webkit-transform: rotate(135deg);
}
.daterangepicker .calendar-table th, .daterangepicker .calendar-table td {
white-space: nowrap;
text-align: center;
vertical-align: middle;
min-width: 32px;
width: 32px;
height: 24px;
line-height: 24px;
font-size: 12px;
border-radius: 4px;
border: 1px solid transparent;
white-space: nowrap;
cursor: pointer;
}
.daterangepicker .calendar-table {
border: 1px solid #fff;
border-radius: 4px;
background-color: #fff;
}
.daterangepicker .calendar-table table {
width: 100%;
margin: 0;
border-spacing: 0;
border-collapse: collapse;
}
.daterangepicker td.available:hover, .daterangepicker th.available:hover {
background-color: #eee;
border-color: transparent;
color: inherit;
}
.daterangepicker td.week, .daterangepicker th.week {
font-size: 80%;
color: #ccc;
}
.daterangepicker td.off, .daterangepicker td.off.in-range, .daterangepicker td.off.start-date, .daterangepicker td.off.end-date {
background-color: #fff;
border-color: transparent;
color: #999;
}
.daterangepicker td.in-range {
background-color: #ebf4f8;
border-color: transparent;
color: #000;
border-radius: 0;
}
.daterangepicker td.start-date {
border-radius: 4px 0 0 4px;
}
.daterangepicker td.end-date {
border-radius: 0 4px 4px 0;
}
.daterangepicker td.start-date.end-date {
border-radius: 4px;
}
.daterangepicker td.active, .daterangepicker td.active:hover {
background-color: #357ebd;
border-color: transparent;
color: #fff;
}
.daterangepicker th.month {
width: auto;
}
.daterangepicker td.disabled, .daterangepicker option.disabled {
color: #999;
cursor: not-allowed;
text-decoration: line-through;
}
.daterangepicker select.monthselect, .daterangepicker select.yearselect {
font-size: 12px;
padding: 1px;
height: auto;
margin: 0;
cursor: default;
}
.daterangepicker select.monthselect {
margin-right: 2%;
width: 56%;
}
.daterangepicker select.yearselect {
width: 40%;
}
.daterangepicker select.hourselect, .daterangepicker select.minuteselect, .daterangepicker select.secondselect, .daterangepicker select.ampmselect {
width: 50px;
margin: 0 auto;
background: #eee;
border: 1px solid #eee;
padding: 2px;
outline: 0;
font-size: 12px;
}
.daterangepicker .calendar-time {
text-align: center;
margin: 4px auto 0 auto;
line-height: 30px;
position: relative;
}
.daterangepicker .calendar-time select.disabled {
color: #ccc;
cursor: not-allowed;
}
.daterangepicker .drp-buttons {
clear: both;
text-align: right;
padding: 8px;
border-top: 1px solid #ddd;
display: none;
line-height: 12px;
vertical-align: middle;
}
.daterangepicker .drp-selected {
display: inline-block;
font-size: 12px;
padding-right: 8px;
}
.daterangepicker .drp-buttons .btn {
margin-left: 8px;
font-size: 12px;
font-weight: bold;
padding: 4px 8px;
}
.daterangepicker.show-ranges.single.rtl .drp-calendar.left {
border-right: 1px solid #ddd;
}
.daterangepicker.show-ranges.single.ltr .drp-calendar.left {
border-left: 1px solid #ddd;
}
.daterangepicker.show-ranges.rtl .drp-calendar.right {
border-right: 1px solid #ddd;
}
.daterangepicker.show-ranges.ltr .drp-calendar.left {
border-left: 1px solid #ddd;
}
.daterangepicker .ranges {
float: none;
text-align: left;
margin: 0;
}
.daterangepicker.show-calendar .ranges {
margin-top: 8px;
}
.daterangepicker .ranges ul {
list-style: none;
margin: 0 auto;
padding: 0;
width: 100%;
}
.daterangepicker .ranges li {
font-size: 12px;
padding: 8px 12px;
cursor: pointer;
}
.daterangepicker .ranges li:hover {
background-color: #eee;
}
.daterangepicker .ranges li.active {
background-color: #08c;
color: #fff;
}
/* Larger Screen Styling */
@media (min-width: 564px) {
.daterangepicker {
width: auto;
}
.daterangepicker .ranges ul {
width: 140px;
}
.daterangepicker.single .ranges ul {
width: 100%;
}
.daterangepicker.single .drp-calendar.left {
clear: none;
}
.daterangepicker.single .ranges, .daterangepicker.single .drp-calendar {
float: left;
}
.daterangepicker {
direction: ltr;
text-align: left;
}
.daterangepicker .drp-calendar.left {
clear: left;
margin-right: 0;
}
.daterangepicker .drp-calendar.left .calendar-table {
border-right: none;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.daterangepicker .drp-calendar.right {
margin-left: 0;
}
.daterangepicker .drp-calendar.right .calendar-table {
border-left: none;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
.daterangepicker .drp-calendar.left .calendar-table {
padding-right: 8px;
}
.daterangepicker .ranges, .daterangepicker .drp-calendar {
float: left;
}
}
@media (min-width: 730px) {
.daterangepicker .ranges {
width: auto;
}
.daterangepicker .ranges {
float: left;
}
.daterangepicker.rtl .ranges {
float: right;
}
.daterangepicker .drp-calendar.left {
clear: none !important;
}
}

File diff suppressed because one or more lines are too long

View file

@ -137,6 +137,14 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
</li>
{{end}}
{{ if and .HasSearcher (.LoggedAdmin.HasPermission "view_events")}}
<li class="nav-item {{if eq .CurrentURL .EventsURL}}active{{end}}">
<a class="nav-link" href="{{.EventsURL}}">
<i class="fas fa-clipboard-list"></i>
<span>{{.EventsTitle}}</span></a>
</li>
{{end}}
{{ if .LoggedAdmin.HasPermission "manage_system"}}
<li class="nav-item {{if eq .CurrentURL .MaintenanceURL}}active{{end}}">
<a class="nav-link" href="{{.MaintenanceURL}}">

View file

@ -0,0 +1,607 @@
<!--
Copyright (C) 2019-2022 Nicola Murino
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, version 3.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
{{template "base" .}}
{{define "title"}}{{.Title}}{{end}}
{{define "extra_css"}}
<link href="{{.StaticURL}}/vendor/datatables/dataTables.bootstrap4.min.css" rel="stylesheet">
<link href="{{.StaticURL}}/vendor/datatables/buttons.bootstrap4.min.css" rel="stylesheet">
<link href="{{.StaticURL}}/vendor/datatables/fixedHeader.bootstrap4.min.css" rel="stylesheet">
<link href="{{.StaticURL}}/vendor/datatables/responsive.bootstrap4.min.css" rel="stylesheet">
<link href="{{.StaticURL}}/vendor/datatables/select.bootstrap4.min.css" rel="stylesheet">
<link href="{{.StaticURL}}/vendor/bootstrap-select/css/bootstrap-select.min.css" rel="stylesheet">
<link href="{{.StaticURL}}/vendor/daterangepicker/daterangepicker.css" rel="stylesheet">
{{end}}
{{define "page_body"}}
<div id="errorMsg" class="card mb-4 border-left-warning" style="display: none;">
<div id="errorTxt" class="card-body text-form-error"></div>
</div>
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">Search logs</h6>
</div>
<div class="card-body">
<div class="form-row">
<div class="form-group col-md-3">
<select class="form-control selectpicker" id="idEventType" name="events_type" onchange="onEventChanged(this.value)">
<option value="1" selected>Fs events</option>
<option value="2">Provider events</option>
</select>
</div>
<div class="form-group col-md-3">
<select class="form-control selectpicker" id="idActions" name="actions" title="Actions" multiple>
</select>
</div>
<div class="form-group col-md-3">
<input type="text" class="form-control" id="idUsername" name="username" placeholder="Username">
</div>
<div class="form-group col-md-3">
<input type="text" class="form-control" id="idIp" name="ip" placeholder="IP address">
</div>
</div>
<div class="form-row">
<div class="form-group col-md-4">
<select class="form-control selectpicker fs-events" id="idProtocols" name="protocols" title="Protocols" multiple>
<option value="SFTP">SFTP</option>
<option value="SCP">SCP</option>
<option value="SSH">SSH</option>
<option value="FTP">FTP</option>
<option value="DAV">DAV</option>
<option value="HTTP">HTTP</option>
<option value="OIDC">OIDC</option>
<option value="HTTPShare">HTTPShare</option>
<option value="DataRetention">DataRetention</option>
<option value="EventAction">EventAction</option>
</select>
</div>
<div class="form-group col-md-4">
<select class="form-control selectpicker fs-events" id="idStatuses" name="statuses" title="Statuses" multiple>
<option value="1">OK</option>
<option value="2">KO</option>
<option value="3">Quota exceeded</option>
</select>
</div>
<div class="form-group col-md-4">
<div class="input-group">
<input type="text" id="dateTimeRange" class="form-control bg-light border-0" aria-describedby="search-button">
<div class="input-group-append">
<button id="search-button" class="btn btn-primary" type="button" onclick="onSearchClicked()">
<i class="fas fa-search fa-sm"></i>
</button>
</div>
</div>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-12">
<button class="btn btn-secondary mt-1 mb-1 px-5" type="button" onclick="onExportClicked()">Export</button>
</div>
</div>
<div class="table-responsive fs-events">
<table class="table table-hover nowrap" id="dataTableFs" width="100%" cellspacing="0">
<thead>
<tr>
<th>ID</th>
<th>Time</th>
<th>Action</th>
<th>Path</th>
<th>User</th>
<th>Proto</th>
<th>IP</th>
<th>Info</th>
</tr>
</thead>
</table>
</div>
<div class="table-responsive provider-events">
<table class="table table-hover nowrap" id="dataTableProvider" width="100%" cellspacing="0">
<thead>
<tr>
<th>ID</th>
<th>Time</th>
<th>Action</th>
<th>Object</th>
<th>User</th>
<th>IP</th>
</tr>
</thead>
</table>
</div>
<div id="paginationContainer" class="m-4 d-none">
<nav aria-label="Pagination">
<ul class="pagination justify-content-end">
<li id="pageItemPrev" class="page-item disabled"><a id="pagePrevious" class="page-link" href="#" onclick="prevClicked()">Previous</a></li>
<li id="pageItemNext" class="page-item disabled"><a id="pageNext" class="page-link" href="#" onclick="nextClicked()">Next</a></li>
</ul>
</nav>
</div>
</div>
</div>
{{end}}
{{define "extra_js"}}
<script src="{{.StaticURL}}/vendor/datatables/jquery.dataTables.min.js"></script>
<script src="{{.StaticURL}}/vendor/datatables/dataTables.bootstrap4.min.js"></script>
<script src="{{.StaticURL}}/vendor/datatables/dataTables.buttons.min.js"></script>
<script src="{{.StaticURL}}/vendor/datatables/buttons.bootstrap4.min.js"></script>
<script src="{{.StaticURL}}/vendor/datatables/buttons.colVis.min.js"></script>
<script src="{{.StaticURL}}/vendor/datatables/dataTables.fixedHeader.min.js"></script>
<script src="{{.StaticURL}}/vendor/datatables/dataTables.responsive.min.js"></script>
<script src="{{.StaticURL}}/vendor/datatables/responsive.bootstrap4.min.js"></script>
<script src="{{.StaticURL}}/vendor/datatables/dataTables.select.min.js"></script>
<script src="{{.StaticURL}}/vendor/datatables/ellipsis.js"></script>
<script src="{{.StaticURL}}/vendor/bootstrap-select/js/bootstrap-select.min.js"></script>
<script src="{{.StaticURL}}/vendor/moment/js/moment.min.js"></script>
<script src="{{.StaticURL}}/vendor/daterangepicker/daterangepicker.min.js"></script>
<script type="text/javascript">
let dateFn = $.fn.dataTable.render.datetime();
let isFsDataTableInitialized = false;
let isProviderDataTableInitialized = false;
let pageSize = 20;
const paginationData = new Map();
function fileSizeIEC(a,b,c,d,e){
return (b=Math,c=b.log,d=1024,e=c(a)/c(d)|0,a/b.pow(d,e)).toFixed(1)
+' '+(e?'KMGTPEZY'[--e]+'iB':'Bytes')
}
function resetPagination() {
$('#pageItemPrev').addClass("disabled");
$('#pageItemNext').addClass("disabled");
$('#paginationContainer').addClass("d-none");
paginationData.delete("firstId");
paginationData.delete("firstTs");
paginationData.delete("lastId");
paginationData.delete("lastTs");
paginationData.set("prevClicked",false);
paginationData.set("nextClicked",false);
}
function prevClicked(){
paginationData.set("prevClicked",true);
paginationData.set("nextClicked",false);
doSearch();
}
function nextClicked(){
paginationData.set("prevClicked",false);
paginationData.set("nextClicked",true);
doSearch();
}
function handleResponseData(data) {
let length = data.length;
let isNext = paginationData.get("nextClicked");
let isPrev = paginationData.get("prevClicked");
if (length > pageSize) {
data.pop();
length--;
if (isPrev || isNext){
$('#pageItemPrev').removeClass("disabled");
}
$('#pageItemNext').removeClass("disabled");
} else {
if (isPrev){
$('#pageItemPrev').addClass("disabled");
$('#pageItemNext').removeClass("disabled");
} else if (isNext){
$('#pageItemPrev').removeClass("disabled");
$('#pageItemNext').addClass("disabled");
} else {
$('#pageItemNext').addClass("disabled");
}
}
if (isPrev){
data = data.reverse();
}
if (length > 0){
paginationData.set("lastId",data[0].id);
paginationData.set("lastTs",data[0].timestamp);
paginationData.set("firstId",data[length-1].id);
paginationData.set("firstTs",data[length-1].timestamp);
$('#paginationContainer').removeClass("d-none");
} else {
resetPagination();
}
return data;
}
function onExportClicked() {
paginationData.set("prevClicked",false);
paginationData.set("nextClicked",false);
let exportURL = getSearchURL(true);
var ts = new Date().getTime().toString();
window.open(`${exportURL}&_=${ts}`);
}
function onSearchClicked() {
resetPagination();
doSearch();
}
function doSearch() {
let eventType = $('#idEventType').val();
let table;
if (eventType == 1){
if (!isFsDataTableInitialized){
initFsDatatable();
return;
}
table = $('#dataTableFs').DataTable();
} else {
if (!isProviderDataTableInitialized){
initProviderDatatable();
return;
}
table = $('#dataTableProvider').DataTable();
}
table.clear().draw();
table.ajax.url(getSearchURL(false)).load();
}
function getSearchURL(csvExport) {
let url = "";
let eventType = $('#idEventType').val();
let order = "DESC";
let limit = pageSize + 1;
if (csvExport){
order = "ASC";
}
if (eventType == 1){
url = "{{.FsEventsSearchURL}}?limit="+limit;
let protocols = [];
$('#idProtocols').find('option:selected').each(function(){
protocols.push($(this).val());
});
if (protocols.length > 0){
url+="&protocols="+encodeURIComponent(String(protocols));
}
let statuses = [];
$('#idStatuses').find('option:selected').each(function(){
statuses.push($(this).val());
});
if (statuses.length > 0){
url+="&statuses="+encodeURIComponent(String(statuses));
}
} else {
url = "{{.ProviderEventsSearchURL}}?limit="+limit;
}
let actions = [];
$('#idActions').find('option:selected').each(function(){
actions.push($(this).val());
});
if (actions.length > 0){
url+="&actions="+encodeURIComponent(String(actions));
}
let username = $('#idUsername').val();
if (username){
url+="&username="+encodeURIComponent(username);
}
let ip = $('#idIp').val();
if (ip){
url+="&ip="+encodeURIComponent(ip);
}
let drp = $('#dateTimeRange').data('daterangepicker');
let excludeIds = [];
let start_ts;
if (!csvExport && paginationData.get("prevClicked") && paginationData.has("lastId") && paginationData.has("lastTs")){
order = "ASC";
start_ts = paginationData.get("lastTs");
excludeIds.push(paginationData.get("lastId"));
} else {
start_ts = drp.startDate.valueOf()*1000000;
}
let end_ts;
if (!csvExport && paginationData.get("nextClicked") && paginationData.has("firstId") && paginationData.has("firstTs")){
end_ts = paginationData.get("firstTs");
excludeIds.push(paginationData.get("firstId"));
} else {
end_ts = drp.endDate.valueOf()*1000000;
}
url+="&start_timestamp="+encodeURIComponent(start_ts);
url+="&end_timestamp="+encodeURIComponent(end_ts);
if (excludeIds.length > 0){
url+="&exclude_ids="+encodeURIComponent(String(excludeIds));
}
url+="&order="+order;
if (csvExport){
url+="&csv_export=true";
}
return url;
}
function initProviderDatatable(){
let tableProvider = $('#dataTableProvider').DataTable({
"ajax": {
"url": getSearchURL(false),
"dataSrc": handleResponseData,
"error": function ($xhr, textStatus, errorThrown) {
$(".dataTables_processing").hide();
var txt = "Failed to get provider events";
if ($xhr) {
var json = $xhr.responseJSON;
if (json) {
if (json.message){
txt += ": " + json.message;
} else {
txt += ": " + json.error;
}
}
}
$('#errorTxt').text(txt);
$('#errorMsg').show();
setTimeout(function () {
$('#errorMsg').hide();
}, 10000);
}
},
"deferRender": true,
"processing": true,
"columns": [
{ "data": "id" },
{
"data": "timestamp",
"render": function (data, type, row) {
if (type === 'display') {
return dateFn(data/1000000,type);
}
return data;
}
},
{
"data": "action"
},
{
"data": "object_type",
"render": function (data, type, row) {
if (type === 'display') {
let ellipsisFn = $.fn.dataTable.render.ellipsis(70, true);
return ellipsisFn(`${data}: ${row["object_name"]}`,type);
}
return data;
}
},
{
"data": "username",
"defaultContent": ""
},
{
"data": "ip",
"defaultContent": ""
}
],
"buttons": [],
"lengthChange": false,
"columnDefs": [
{
"targets": [0],
"visible": false,
"searchable": false
},
],
"responsive": true,
"searching": false,
"paging": false,
"info": false,
"ordering": false,
"language": {
"loadingRecords": "",
"emptyTable": "No logs found"
}
});
new $.fn.dataTable.FixedHeader(tableProvider);
isProviderDataTableInitialized = true;
}
function initFsDatatable(){
let tableFs = $('#dataTableFs').DataTable({
"ajax": {
"url": getSearchURL(false),
"dataSrc": handleResponseData,
"error": function ($xhr, textStatus, errorThrown) {
$(".dataTables_processing").hide();
var txt = "Failed to get filesystem events";
if ($xhr) {
let json = $xhr.responseJSON;
if (json) {
if (json.message){
txt += ": " + json.message;
} else {
txt += ": " + json.error;
}
}
}
$('#errorTxt').text(txt);
$('#errorMsg').show();
setTimeout(function () {
$('#errorMsg').hide();
}, 10000);
}
},
"deferRender": true,
"processing": true,
"columns": [
{ "data": "id" },
{
"data": "timestamp",
"render": function (data, type, row) {
if (type === 'display') {
return dateFn(data/1000000,type);
}
return data;
}
},
{
"data": "action"
},
{
"data": "virtual_path",
"render": function (data, type, row) {
if (type === 'display') {
let ellipsisFn = $.fn.dataTable.render.ellipsis(70, true);
if (row["virtual_target_path"]){
return ellipsisFn(`${data} => ${row["virtual_target_path"]}`,type);
}
return ellipsisFn(data,type);
}
return data;
}
},
{
"data": "username",
"defaultContent": ""
},
{
"data": "protocol",
"defaultContent": ""
},
{
"data": "ip",
"defaultContent": ""
},
{
"data": "status",
"render": function (data, type, row) {
if (type === 'display') {
let info = 'OK';
if (data == 2){
info = 'KO';
} else if (data == 3){
info = 'Quota exceeded'
}
if (row["file_size"]){
let humanSize = fileSizeIEC(row["file_size"]);
info+=`. ${humanSize}`
}
if (row["ssh_cmd"]){
info+=`. Command: ${row["ssh_cmd"]}`
}
return info;
}
return data;
}
}
],
"buttons": [],
"lengthChange": false,
"columnDefs": [
{
"targets": [0],
"visible": false,
"searchable": false
},
],
"responsive": true,
"searching": false,
"paging": false,
"info": false,
"ordering": false,
"language": {
"loadingRecords": "",
"emptyTable": "No logs found"
}
});
new $.fn.dataTable.FixedHeader(tableFs);
isFsDataTableInitialized = true;
}
function selectFsEvents(){
let idActions = $('#idActions');
idActions.selectpicker('deselectAll');
idActions.find('option').remove();
idActions.find('li').remove();
idActions.append($('<option>').val('upload').text('Upload'));
idActions.append($('<option>').val('download').text('Download'));
idActions.append($('<option>').val('mkdir').text('Mkdir'));
idActions.append($('<option>').val('rmdir').text('Rmdir'));
idActions.append($('<option>').val('rename').text('Rename'));
idActions.append($('<option>').val('delete').text('Delete'));
idActions.append($('<option>').val('first-upload').text('First upload'));
idActions.append($('<option>').val('first-download').text('First download'));
idActions.append($('<option>').val('pre-upload').text('Pre-upload'));
idActions.append($('<option>').val('pre-download').text('Pre-download'));
idActions.append($('<option>').val('pre-delete').text('Pre-delete'));
idActions.append($('<option>').val('ssh_cmd').text('SSH command'));
idActions.selectpicker('refresh');
$('#idUsername').val("");
$('#idIp').val("");
$('.provider-events').hide();
$('.fs-events').show();
onSearchClicked();
}
function selectProviderEvents(){
let idActions = $('#idActions');
idActions.selectpicker('deselectAll');
idActions.find('option').remove();
idActions.find('li').remove();
idActions.append($('<option>').val('add').text('Add'));
idActions.append($('<option>').val('update').text('Update'));
idActions.append($('<option>').val('delete').text('Delete'));
idActions.selectpicker('refresh');
$('#idUsername').val("");
$('#idIp').val("");
$('.fs-events').hide();
$('.provider-events').show();
onSearchClicked();
}
function onEventChanged(val){
switch (val){
case '1':
case 1:
selectFsEvents();
break;
case '2':
case 2:
selectProviderEvents();
break;
default:
console.log(`unsupported event type: ${val}`);
}
}
$(document).ready(function () {
$('#dateTimeRange').daterangepicker({
timePicker: true,
timePicker24Hour: true,
opens: 'left',
startDate: moment().add(-1,'hour'),
endDate: moment(),
locale: {
format: 'DD/MM HH:mm'
}
});
$.fn.dataTable.ext.errMode = 'none';
onEventChanged('1');
});
</script>
{{end}}

View file

@ -243,7 +243,8 @@ function deleteAction() {
enabled: false
};
var table = $('#dataTable').DataTable({
let dateFn = $.fn.dataTable.render.datetime();
let table = $('#dataTable').DataTable({
"select": {
"style": "single",
"blurable": true
@ -296,11 +297,10 @@ function deleteAction() {
return data;
}
if (row[0] !== ""){
var dateFn = $.fn.dataTable.render.datetime();
var formattedDate = dateFn(row[0], type);
let formattedDate = dateFn(row[0], type);
data = `${data}. Updated at: ${formattedDate}`;
}
var ellipsisFn = $.fn.dataTable.render.ellipsis(60, true);
let ellipsisFn = $.fn.dataTable.render.ellipsis(60, true);
return ellipsisFn(data, type);
}
}

View file

@ -263,7 +263,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
enabled: false
};
var table = $('#dataTable').DataTable({
let dateFn = $.fn.dataTable.render.datetime();
let table = $('#dataTable').DataTable({
"select": {
"style": "single",
"blurable": true
@ -318,11 +319,10 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
return data;
}
if (row[12] !== ""){
var dateFn = $.fn.dataTable.render.datetime();
var formattedDate = dateFn(row[12], type);
data = `${data}. Updated at: ${formattedDate}`;
}
var ellipsisFn = $.fn.dataTable.render.ellipsis(70, true);
let ellipsisFn = $.fn.dataTable.render.ellipsis(70, true);
return ellipsisFn(data, type);
}
},

View file

@ -953,7 +953,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
var downloadURL = '{{.DownloadURL}}';
var currentDir = '{{.CurrentDir}}';
var ts = new Date().getTime().toString();
window.location = `${downloadURL}?path=${currentDir}&files=${files}&_=${ts}`;
window.open(`${downloadURL}?path=${currentDir}&files=${files}&_=${ts}`);
},
enabled: false
};
@ -1019,7 +1019,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
var shareURL = '{{.ShareURL}}';
var currentDir = '{{.CurrentDir}}';
var ts = new Date().getTime().toString();
window.location = `${shareURL}?path=${currentDir}&files=${files}&_=${ts}`;
window.open(`${shareURL}?path=${currentDir}&files=${files}&_=${ts}`,'_blank');
},
enabled: false
};

View file

@ -410,7 +410,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
var downloadURL = '{{.DownloadURL}}';
var currentDir = '{{.CurrentDir}}';
var ts = new Date().getTime().toString();
window.location = `${downloadURL}?path=${currentDir}&files=${files}&_=${ts}`;
window.open(`${downloadURL}?path=${currentDir}&files=${files}&_=${ts}`);
},
enabled: false
};

View file

@ -4,23 +4,23 @@ go 1.19
require (
github.com/hashicorp/go-plugin v1.4.6
github.com/sftpgo/sdk v0.1.3-0.20221203095324-2feef3600930
github.com/sftpgo/sdk v0.1.3-0.20221205110949-c15308d4236e
)
require (
github.com/fatih/color v1.13.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/hashicorp/go-hclog v1.3.1 // indirect
github.com/hashicorp/go-hclog v1.4.0 // indirect
github.com/hashicorp/yamux v0.1.1 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
github.com/oklog/run v1.1.0 // indirect
golang.org/x/net v0.2.0 // indirect
golang.org/x/sys v0.2.0 // indirect
golang.org/x/text v0.4.0 // indirect
google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd // indirect
golang.org/x/net v0.4.0 // indirect
golang.org/x/sys v0.3.0 // indirect
golang.org/x/text v0.5.0 // indirect
google.golang.org/genproto v0.0.0-20221206210731-b1a01be3a5f6 // indirect
google.golang.org/grpc v1.51.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
)

View file

@ -9,8 +9,8 @@ github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu
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/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/hashicorp/go-hclog v1.3.1 h1:vDwF1DFNZhntP4DAjuTpOw3uEgMUpXh1pB5fW9DqHpo=
github.com/hashicorp/go-hclog v1.3.1/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
github.com/hashicorp/go-hclog v1.4.0 h1:ctuWFGrhFha8BnnzxqeRGidlEcQkDyL5u8J8t5eA11I=
github.com/hashicorp/go-hclog v1.4.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
github.com/hashicorp/go-plugin v1.4.6 h1:MDV3UrKQBM3du3G7MApDGvOsMYy3JQJ4exhSoKBAeVA=
github.com/hashicorp/go-plugin v1.4.6/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s=
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
@ -30,26 +30,26 @@ github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sftpgo/sdk v0.1.3-0.20221203095324-2feef3600930 h1:znJ52fQBSAQhacaQvZAfkpTioqpcutDREM/H8NttKzU=
github.com/sftpgo/sdk v0.1.3-0.20221203095324-2feef3600930/go.mod h1:S2S/Q9fgUpXmL11YoCCt0hyCkEwH1LzQM/6QVsbUCFg=
github.com/sftpgo/sdk v0.1.3-0.20221205110949-c15308d4236e h1:F3G/BReUSU8TX6Kmk0moQgQAk9Ouiv2I+pg//o1IR6U=
github.com/sftpgo/sdk v0.1.3-0.20221205110949-c15308d4236e/go.mod h1:S2S/Q9fgUpXmL11YoCCt0hyCkEwH1LzQM/6QVsbUCFg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU=
golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd h1:OjndDrsik+Gt+e6fs45z9AxiewiKyLKYpA45W5Kpkks=
google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE=
google.golang.org/genproto v0.0.0-20221206210731-b1a01be3a5f6 h1:AGXp12e/9rItf6/4QymU7WsAUwCf+ICW75cuR91nJIc=
google.golang.org/genproto v0.0.0-20221206210731-b1a01be3a5f6/go.mod h1:1dOng4TWOomJrDGhpXjfCD35wQC6jnC7HpRmOFRqEV0=
google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U=
google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=

View file

@ -4,22 +4,22 @@ go 1.19
require (
github.com/hashicorp/go-plugin v1.4.6
github.com/sftpgo/sdk v0.1.2
github.com/sftpgo/sdk v0.1.3-0.20221205110949-c15308d4236e
)
require (
github.com/fatih/color v1.13.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/hashicorp/go-hclog v1.3.1 // indirect
github.com/hashicorp/go-hclog v1.4.0 // indirect
github.com/hashicorp/yamux v0.1.1 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
github.com/oklog/run v1.1.0 // indirect
golang.org/x/net v0.2.0 // indirect
golang.org/x/sys v0.2.0 // indirect
golang.org/x/text v0.4.0 // indirect
google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd // indirect
golang.org/x/net v0.4.0 // indirect
golang.org/x/sys v0.3.0 // indirect
golang.org/x/text v0.5.0 // indirect
google.golang.org/genproto v0.0.0-20221206210731-b1a01be3a5f6 // indirect
google.golang.org/grpc v1.51.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
)

View file

@ -8,8 +8,8 @@ github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw
github.com/golang/protobuf v1.5.2/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.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/hashicorp/go-hclog v1.3.1 h1:vDwF1DFNZhntP4DAjuTpOw3uEgMUpXh1pB5fW9DqHpo=
github.com/hashicorp/go-hclog v1.3.1/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
github.com/hashicorp/go-hclog v1.4.0 h1:ctuWFGrhFha8BnnzxqeRGidlEcQkDyL5u8J8t5eA11I=
github.com/hashicorp/go-hclog v1.4.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
github.com/hashicorp/go-plugin v1.4.6 h1:MDV3UrKQBM3du3G7MApDGvOsMYy3JQJ4exhSoKBAeVA=
github.com/hashicorp/go-plugin v1.4.6/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s=
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
@ -29,27 +29,27 @@ github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sftpgo/sdk v0.1.2 h1:j4V63RuVcYfJAOWV0zRUofa1PlQvKU2ujly0lB7quVA=
github.com/sftpgo/sdk v0.1.2/go.mod h1:PTp1TfXa+95wHw9yuZu7BA3vmzLqbRkz3gBmMNnwFQg=
github.com/sftpgo/sdk v0.1.3-0.20221205110949-c15308d4236e h1:F3G/BReUSU8TX6Kmk0moQgQAk9Ouiv2I+pg//o1IR6U=
github.com/sftpgo/sdk v0.1.3-0.20221205110949-c15308d4236e/go.mod h1:S2S/Q9fgUpXmL11YoCCt0hyCkEwH1LzQM/6QVsbUCFg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU=
golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd h1:OjndDrsik+Gt+e6fs45z9AxiewiKyLKYpA45W5Kpkks=
google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE=
google.golang.org/genproto v0.0.0-20221206210731-b1a01be3a5f6 h1:AGXp12e/9rItf6/4QymU7WsAUwCf+ICW75cuR91nJIc=
google.golang.org/genproto v0.0.0-20221206210731-b1a01be3a5f6/go.mod h1:1dOng4TWOomJrDGhpXjfCD35wQC6jnC7HpRmOFRqEV0=
google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U=
google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=