From fa0ca8fe899f6438d3303b1bd666d7b8d8239c62 Mon Sep 17 00:00:00 2001 From: Nicola Murino Date: Tue, 8 Feb 2022 12:43:08 +0100 Subject: [PATCH] quota summary and docs improvements Signed-off-by: Nicola Murino --- README.md | 16 ++++++------- dataprovider/user.go | 54 ++++++++++++++++++++++++++++++++------------ go.mod | 10 ++++---- go.sum | 20 ++++++++-------- httpd/server.go | 4 ++-- 5 files changed, 65 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index e1164a27..27150d70 100644 --- a/README.md +++ b/README.md @@ -34,19 +34,19 @@ Several storage backends are supported: local filesystem, encrypted local filesy - Bandwidth throttling, with separate settings for upload and download and overrides based on the client's IP address. - Data transfer bandwidth limits, with total limit or separate settings for uploads and downloads and overrides based on the client's IP address. Limits can be reset using the REST API. - Per-protocol [rate limiting](./docs/rate-limiting.md) is supported and can be optionally connected to the built-in defender to automatically block hosts that repeatedly exceed the configured limit. -- Per user maximum concurrent sessions. -- Per user and global IP filters: login can be restricted to specific ranges of IP addresses or to a specific IP address. -- Per user and per directory shell like patterns filters: files can be allowed or denied based on shell like patterns. +- Per-user maximum concurrent sessions. +- Per-user and global IP filters: login can be restricted to specific ranges of IP addresses or to a specific IP address. +- Per-user and per-directory shell like patterns filters: files can be allowed, denied or hidden based on shell like patterns. - Automatically terminating idle connections. - Automatic blocklist management using the built-in [defender](./docs/defender.md). - Atomic uploads are configurable. -- Per user files/folders ownership mapping: you can map all the users to the system account that runs SFTPGo (all platforms are supported) or you can run SFTPGo as root user and map each user or group of users to a different system account (\*NIX only). +- Per-user files/folders ownership mapping: you can map all the users to the system account that runs SFTPGo (all platforms are supported) or you can run SFTPGo as root user and map each user or group of users to a different system account (\*NIX only). - Support for Git repositories over SSH. - SCP and rsync are supported. - FTP/S is supported. You can configure the FTP service to require TLS for both control and data connections. - [WebDAV](./docs/webdav.md) is supported. - Two-Way TLS authentication, aka TLS with client certificate authentication, is supported for REST API/Web Admin, FTPS and WebDAV over HTTPS. -- Per user protocols restrictions. You can configure the allowed protocols (SSH/FTP/WebDAV) for each user. +- Per-user protocols restrictions. You can configure the allowed protocols (SSH/FTP/WebDAV) for each user. - [Prometheus metrics](./docs/metrics.md) are exposed. - Support for HAProxy PROXY protocol: you can proxy and/or load balance the SFTP/SCP/FTP/WebDAV service without losing the information about the client's address. - Easy [migration](./examples/convertusers) from Linux system user accounts. @@ -265,13 +265,13 @@ Adding new storage backends is quite easy: - update the web interface and the REST API CLI - add the flags for the new storage backed to the `portable` mode -Anyway, some backends require a pay per use account (or they offer free account for a limited time period only). To be able to add support for such backends or to review pull requests, please provide a test account. The test account must be available for enough time to be able to maintain the backend and do basic tests before each new release. +Anyway, some backends require a pay per-use account (or they offer free account for a limited time period only). To be able to add support for such backends or to review pull requests, please provide a test account. The test account must be available for enough time to be able to maintain the backend and do basic tests before each new release. ## Brute force protection -The [connection failed logs](./docs/logs.md) can be used for integration in tools such as [Fail2ban](http://www.fail2ban.org/). Example of [jails](./fail2ban/jails) and [filters](./fail2ban/filters) working with `systemd`/`journald` are available in fail2ban directory. +SFTPGo supports a built-in [defender](./docs/defender.md). -You can also use the built-in [defender](./docs/defender.md). +Alternately you can use the [connection failed logs](./docs/logs.md) for integration in tools such as [Fail2ban](http://www.fail2ban.org/). Example of [jails](./fail2ban/jails) and [filters](./fail2ban/filters) working with `systemd`/`journald` are available in fail2ban directory. ## Account's configuration properties diff --git a/dataprovider/user.go b/dataprovider/user.go index 54ff1623..0e0b69ee 100644 --- a/dataprovider/user.go +++ b/dataprovider/user.go @@ -11,7 +11,6 @@ import ( "os" "path" "path/filepath" - "strconv" "strings" "time" @@ -1125,22 +1124,49 @@ func (u *User) GetDataTransferLimits(clientIP string) (int64, int64, int64) { // GetQuotaSummary returns used quota and limits if defined func (u *User) GetQuotaSummary() string { - var result string - result = "Files: " + strconv.Itoa(u.UsedQuotaFiles) - if u.QuotaFiles > 0 { - result += "/" + strconv.Itoa(u.QuotaFiles) - } - if u.UsedQuotaSize > 0 || u.QuotaSize > 0 { - result += ". Size: " + util.ByteCountIEC(u.UsedQuotaSize) - if u.QuotaSize > 0 { - result += "/" + util.ByteCountIEC(u.QuotaSize) + var sb strings.Builder + + addSection := func() { + if sb.Len() > 0 { + sb.WriteString(". ") } } - if u.LastQuotaUpdate > 0 { - t := util.GetTimeFromMsecSinceEpoch(u.LastQuotaUpdate) - result += fmt.Sprintf(". Last update: %v ", t.Format("2006-01-02 15:04")) // YYYY-MM-DD HH:MM + + if u.UsedQuotaFiles > 0 || u.QuotaFiles > 0 { + sb.WriteString(fmt.Sprintf("Files: %v", u.UsedQuotaFiles)) + if u.QuotaFiles > 0 { + sb.WriteString(fmt.Sprintf("/%v", u.QuotaFiles)) + } } - return result + if u.UsedQuotaSize > 0 || u.QuotaSize > 0 { + addSection() + sb.WriteString(fmt.Sprintf("Size: %v", util.ByteCountIEC(u.UsedQuotaSize))) + if u.QuotaSize > 0 { + sb.WriteString(fmt.Sprintf("/%v", util.ByteCountIEC(u.QuotaSize))) + } + } + if u.TotalDataTransfer > 0 { + addSection() + total := u.UsedDownloadDataTransfer + u.UsedUploadDataTransfer + sb.WriteString(fmt.Sprintf("Transfer: %v/%v", util.ByteCountIEC(total), + util.ByteCountIEC(u.TotalDataTransfer*1048576))) + } + if u.UploadDataTransfer > 0 { + addSection() + sb.WriteString(fmt.Sprintf("UL: %v/%v", util.ByteCountIEC(u.UsedUploadDataTransfer), + util.ByteCountIEC(u.UploadDataTransfer*1048576))) + } + if u.DownloadDataTransfer > 0 { + addSection() + sb.WriteString(fmt.Sprintf("DL: %v/%v", util.ByteCountIEC(u.UsedDownloadDataTransfer), + util.ByteCountIEC(u.DownloadDataTransfer*1048576))) + } + if u.LastQuotaUpdate > 0 { + addSection() + t := util.GetTimeFromMsecSinceEpoch(u.LastQuotaUpdate) + sb.WriteString(fmt.Sprintf("Last update: %v", t.Format("2006-01-02 15:04"))) // YYYY-MM-DD HH:MM + } + return sb.String() } // GetPermissionsAsString returns the user's permissions as comma separated string diff --git a/go.mod b/go.mod index d5e91fb4..49aa178a 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/Azure/azure-storage-blob-go v0.14.0 github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962 github.com/alexedwards/argon2id v0.0.0-20211130144151-3585854a6387 - github.com/aws/aws-sdk-go v1.42.47 + github.com/aws/aws-sdk-go v1.42.48 github.com/cockroachdb/cockroach-go/v2 v2.2.6 github.com/eikenb/pipeat v0.0.0-20210603033007-44fc3ffce52b github.com/fclairamb/ftpserverlib v0.17.0 @@ -54,7 +54,7 @@ require ( gocloud.dev v0.24.0 golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd - golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a + golang.org/x/sys v0.0.0-20220207234003-57398862261d golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 google.golang.org/api v0.67.0 gopkg.in/natefinch/lumberjack.v2 v2.0.0 @@ -86,7 +86,7 @@ require ( github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect - github.com/klauspost/cpuid/v2 v2.0.10 // indirect + github.com/klauspost/cpuid/v2 v2.0.11 // indirect github.com/kr/fs v0.1.0 // indirect github.com/lestrrat-go/backoff/v2 v2.0.8 // indirect github.com/lestrrat-go/blackmagic v1.0.0 // indirect @@ -99,7 +99,7 @@ require ( github.com/mattn/go-ieproxy v0.0.3 // indirect github.com/mattn/go-isatty v0.0.14 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect - github.com/miekg/dns v1.1.45 // indirect + github.com/miekg/dns v1.1.46 // indirect github.com/minio/sha256-simd v1.0.0 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mitchellh/mapstructure v1.4.3 // indirect @@ -126,7 +126,7 @@ require ( golang.org/x/tools v0.1.9 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20220204002441-d6cc3cc0770e // indirect + google.golang.org/genproto v0.0.0-20220207185906-7721543eae58 // indirect google.golang.org/grpc v1.44.0 // indirect google.golang.org/protobuf v1.27.1 // indirect gopkg.in/ini.v1 v1.66.3 // indirect diff --git a/go.sum b/go.sum index d9161164..1dd963d5 100644 --- a/go.sum +++ b/go.sum @@ -141,8 +141,8 @@ github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgI github.com/aws/aws-sdk-go v1.15.27/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= github.com/aws/aws-sdk-go v1.37.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/aws/aws-sdk-go v1.40.34/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= -github.com/aws/aws-sdk-go v1.42.47 h1:Faabrbp+bOBiZjHje7Hbhvni212aQYQIXZMruwkgmmA= -github.com/aws/aws-sdk-go v1.42.47/go.mod h1:OGr6lGMAKGlG9CVrYnWYDKIyb829c6EVBRjxqjmPepc= +github.com/aws/aws-sdk-go v1.42.48 h1:8ZVBAsA9X2eCpSr/8SrWDk4BOT91wRdqxpAog875+K0= +github.com/aws/aws-sdk-go v1.42.48/go.mod h1:OGr6lGMAKGlG9CVrYnWYDKIyb829c6EVBRjxqjmPepc= github.com/aws/aws-sdk-go-v2 v1.9.0/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= github.com/aws/aws-sdk-go-v2/config v1.7.0/go.mod h1:w9+nMZ7soXCe5nT46Ri354SNhXDQ6v+V5wqDjnZE+GY= github.com/aws/aws-sdk-go-v2/credentials v1.4.0/go.mod h1:dgGR+Qq7Wjcd4AOAW5Rf5Tnv3+x7ed6kETXyS9WCuAY= @@ -511,8 +511,8 @@ github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47e github.com/klauspost/compress v1.14.2 h1:S0OHlFk/Gbon/yauFJ4FfJJF5V0fc5HbBTJazi28pRw= github.com/klauspost/compress v1.14.2/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.0.10 h1:fv5GKR+e2UgD+gcxQECVT5rBwAmlFLl2mkKm7WK3ODY= -github.com/klauspost/cpuid/v2 v2.0.10/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= +github.com/klauspost/cpuid/v2 v2.0.11 h1:i2lw1Pm7Yi/4O6XCSyJWqEHI2MDw2FzUK6o/D21xn2A= +github.com/klauspost/cpuid/v2 v2.0.11/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= 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= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -590,8 +590,8 @@ github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3N github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= -github.com/miekg/dns v1.1.45 h1:g5fRIhm9nx7g8osrAvgb16QJfmyMsyOCb+J7LSv+Qzk= -github.com/miekg/dns v1.1.45/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= +github.com/miekg/dns v1.1.46 h1:uzwpxRtSVxtcIZmz/4Uz6/Rn7G11DvsaslXoy5LxQio= +github.com/miekg/dns v1.1.46/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= github.com/minio/sio v0.3.0 h1:syEFBewzOMOYVzSTFpp1MqpSZk8rUNbz8VIIc+PNzus= @@ -949,8 +949,8 @@ golang.org/x/sys v0.0.0-20220110181412-a018aaa089fe/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a h1:ppl5mZgokTT8uPkmYOyEUmPTr3ypaKkg5eFOGrAmxxE= -golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220207234003-57398862261d h1:Bm7BNOQt2Qv7ZqysjeLjgCBanX+88Z/OtdvsrEv1Djc= +golang.org/x/sys v0.0.0-20220207234003-57398862261d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1170,8 +1170,8 @@ google.golang.org/genproto v0.0.0-20220111164026-67b88f271998/go.mod h1:5CzLGKJ6 google.golang.org/genproto v0.0.0-20220114231437-d2e6a121cae0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220201184016-50beb8ab5c44/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220204002441-d6cc3cc0770e h1:hXl9hnyOkeznztYpYxVPAVZfPzcbO6Q0C+nLXodza8k= -google.golang.org/genproto v0.0.0-20220204002441-d6cc3cc0770e/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220207185906-7721543eae58 h1:i67FGOy2/zGfhE3YgHdrOrcFbOBhqdcRoBrsDqSQrOI= +google.golang.org/genproto v0.0.0-20220207185906-7721543eae58/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= diff --git a/httpd/server.go b/httpd/server.go index 8be9bb8f..91517980 100644 --- a/httpd/server.go +++ b/httpd/server.go @@ -1007,7 +1007,7 @@ func (s *httpdServer) initializeRouter() { s.router.Get(sharesPath+"/{id}", downloadFromShare) s.router.Post(sharesPath+"/{id}", uploadFilesToShare) s.router.Post(sharesPath+"/{id}/{name}", uploadFileToShare) - s.router.Get(sharesPath+"/{id}/dirs", readBrowsableShareContents) + s.router.With(compressor.Handler).Get(sharesPath+"/{id}/dirs", readBrowsableShareContents) s.router.Get(sharesPath+"/{id}/files", downloadBrowsableSharedFile) s.router.Get(tokenPath, s.getToken) @@ -1229,7 +1229,7 @@ func (s *httpdServer) initializeRouter() { // share API exposed to external users s.router.Get(webClientPubSharesPath+"/{id}", downloadFromShare) s.router.Get(webClientPubSharesPath+"/{id}/browse", s.handleShareGetFiles) - s.router.Get(webClientPubSharesPath+"/{id}/dirs", s.handleShareGetDirContents) + s.router.With(compressor.Handler).Get(webClientPubSharesPath+"/{id}/dirs", s.handleShareGetDirContents) s.router.Post(webClientPubSharesPath+"/{id}", uploadFilesToShare) s.router.Post(webClientPubSharesPath+"/{id}/{name}", uploadFileToShare)