mirror of
https://github.com/drakkan/sftpgo.git
synced 2024-11-25 00:50:31 +00:00
WebDAV: improve TLS certificate authentication
For each user you can now configure: - TLS certificate auth - TLS certificate auth and password - Password auth For TLS certificate auth, the certificate common name is used as username
This commit is contained in:
parent
901cafc6da
commit
534b253c20
19 changed files with 816 additions and 151 deletions
|
@ -570,6 +570,80 @@ func CheckAdminAndPass(username, password, ip string) (Admin, error) {
|
|||
return provider.validateAdminAndPass(username, password, ip)
|
||||
}
|
||||
|
||||
// CheckCachedUserCredentials checks the credentials for a cached user
|
||||
func CheckCachedUserCredentials(user *CachedUser, password, loginMethod, protocol string, tlsCert *x509.Certificate) error {
|
||||
if loginMethod != LoginMethodPassword {
|
||||
_, err := checkUserAndTLSCertificate(&user.User, protocol, tlsCert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if loginMethod == LoginMethodTLSCertificate {
|
||||
if !user.User.IsLoginMethodAllowed(LoginMethodTLSCertificate, nil) {
|
||||
return fmt.Errorf("Certificate login method is not allowed for user %#v", user.User.Username)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if err := checkLoginConditions(&user.User); err != nil {
|
||||
return err
|
||||
}
|
||||
if password == "" {
|
||||
return ErrInvalidCredentials
|
||||
}
|
||||
if user.Password != "" {
|
||||
if password == user.Password {
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
if ok, _ := isPasswordOK(&user.User, password); ok {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return ErrInvalidCredentials
|
||||
}
|
||||
|
||||
// CheckCompositeCredentials checks multiple credentials.
|
||||
// WebDAV users can send both a password and a TLS certificate within the same request
|
||||
func CheckCompositeCredentials(username, password, ip, loginMethod, protocol string, tlsCert *x509.Certificate) (User, string, error) {
|
||||
if loginMethod == LoginMethodPassword {
|
||||
user, err := CheckUserAndPass(username, password, ip, protocol)
|
||||
return user, loginMethod, err
|
||||
}
|
||||
user, err := CheckUserBeforeTLSAuth(username, ip, protocol, tlsCert)
|
||||
if err != nil {
|
||||
return user, loginMethod, err
|
||||
}
|
||||
if !user.IsTLSUsernameVerificationEnabled() {
|
||||
// for backward compatibility with 2.0.x we only check the password and change the login method here
|
||||
// in future updates we have to return an error
|
||||
user, err := CheckUserAndPass(username, password, ip, protocol)
|
||||
return user, LoginMethodPassword, err
|
||||
}
|
||||
user, err = checkUserAndTLSCertificate(&user, protocol, tlsCert)
|
||||
if err != nil {
|
||||
return user, loginMethod, err
|
||||
}
|
||||
if loginMethod == LoginMethodTLSCertificate && !user.IsLoginMethodAllowed(LoginMethodTLSCertificate, nil) {
|
||||
return user, loginMethod, fmt.Errorf("Certificate login method is not allowed for user %#v", user.Username)
|
||||
}
|
||||
if loginMethod == LoginMethodTLSCertificateAndPwd {
|
||||
if config.ExternalAuthHook != "" && (config.ExternalAuthScope == 0 || config.ExternalAuthScope&1 != 0) {
|
||||
user, err = doExternalAuth(username, password, nil, "", ip, protocol, nil)
|
||||
if err != nil {
|
||||
return user, loginMethod, err
|
||||
}
|
||||
}
|
||||
if config.PreLoginHook != "" {
|
||||
user, err = executePreLoginHook(username, LoginMethodPassword, ip, protocol)
|
||||
if err != nil {
|
||||
return user, loginMethod, err
|
||||
}
|
||||
}
|
||||
user, err = checkUserAndPass(&user, password, ip, protocol)
|
||||
}
|
||||
return user, loginMethod, err
|
||||
}
|
||||
|
||||
// CheckUserBeforeTLSAuth checks if a user exits before trying mutual TLS
|
||||
func CheckUserBeforeTLSAuth(username, ip, protocol string, tlsCert *x509.Certificate) (User, error) {
|
||||
if config.ExternalAuthHook != "" && (config.ExternalAuthScope == 0 || config.ExternalAuthScope&8 != 0) {
|
||||
|
@ -1458,7 +1532,7 @@ func checkUserAndTLSCertificate(user *User, protocol string, tlsCert *x509.Certi
|
|||
return *user, err
|
||||
}
|
||||
switch protocol {
|
||||
case "FTP":
|
||||
case "FTP", "DAV":
|
||||
if user.Filters.TLSUsername == TLSUsernameCN {
|
||||
if user.Username == tlsCert.Subject.CommonName {
|
||||
return *user, nil
|
||||
|
|
|
@ -6,7 +6,7 @@ To enable dynamic user modification, you must set the absolute path of your prog
|
|||
The external program can read the following environment variables to get info about the user trying to login:
|
||||
|
||||
- `SFTPGO_LOGIND_USER`, it contains the user trying to login serialized as JSON. A JSON serialized user id equal to zero means the user does not exist inside SFTPGo
|
||||
- `SFTPGO_LOGIND_METHOD`, possible values are: `password`, `publickey` and `keyboard-interactive`
|
||||
- `SFTPGO_LOGIND_METHOD`, possible values are: `password`, `publickey`, `keyboard-interactive`, `TLSCertificate`
|
||||
- `SFTPGO_LOGIND_IP`, ip address of the user trying to login
|
||||
- `SFTPGO_LOGIND_PROTOCOL`, possible values are `SSH`, `FTP`, `DAV`
|
||||
|
||||
|
|
|
@ -132,7 +132,7 @@ The configuration file contains the following sections:
|
|||
- `port`, integer. The port used for serving WebDAV requests. 0 means disabled. Default: 0.
|
||||
- `address`, string. Leave blank to listen on all available network interfaces. Default: "".
|
||||
- `enable_https`, boolean. Set to `true` and provide both a certificate and a key file to enable HTTPS connection for this binding. Default `false`.
|
||||
- `client_auth_type`, integer. Set to `1` to require client certificate authentication in addition to basic authentication. You need to define at least a certificate authority for this to work. Default: 0.
|
||||
- `client_auth_type`, integer. Set to `1` to require a client certificate and verify it. Set to `2` to request a client certificate during the TLS handshake and verify it if given, in this mode the client is allowed not to send a certificate. At least one certification authority must be defined in order to verify client certificates. If no certification authority is defined, this setting is ignored. Default: 0.
|
||||
- `tls_cipher_suites`, list of strings. List of supported cipher suites for TLS version 1.2. If empty, a default list of secure cipher suites is used, with a preference order based on hardware performance. Note that TLS 1.3 ciphersuites are not configurable. The supported ciphersuites names are defined [here](https://github.com/golang/go/blob/master/src/crypto/tls/cipher_suites.go#L52). Any invalid name will be silently ignored. The order matters, the ciphers listed first will be the preferred ones. Default: empty.
|
||||
- `bind_port`, integer. Deprecated, please use `bindings`.
|
||||
- `bind_address`, string. Deprecated, please use `bindings`.
|
||||
|
|
|
@ -10,7 +10,7 @@ If the hook defines an external program it can reads the following environment v
|
|||
|
||||
- `SFTPGO_LOGIND_USER`, it contains the user serialized as JSON. The username is empty if the connection is closed for authentication timeout
|
||||
- `SFTPGO_LOGIND_IP`
|
||||
- `SFTPGO_LOGIND_METHOD`, possible values are `publickey`, `password`, `keyboard-interactive`, `publickey+password`, `publickey+keyboard-interactive` or `no_auth_tryed`
|
||||
- `SFTPGO_LOGIND_METHOD`, possible values are `publickey`, `password`, `keyboard-interactive`, `publickey+password`, `publickey+keyboard-interactive`, `TLSCertificate`, `TLSCertificate+password` or `no_auth_tryed`
|
||||
- `SFTPGO_LOGIND_STATUS`, 1 means login OK, 0 login KO
|
||||
- `SFTPGO_LOGIND_PROTOCOL`, possible values are `SSH`, `FTP`, `DAV`
|
||||
|
||||
|
|
17
go.mod
17
go.mod
|
@ -1,6 +1,6 @@
|
|||
module github.com/drakkan/sftpgo
|
||||
|
||||
go 1.15
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.78.0 // indirect
|
||||
|
@ -9,16 +9,17 @@ require (
|
|||
github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962
|
||||
github.com/StackExchange/wmi v0.0.0-20210224194228-fe8f1750fd46 // indirect
|
||||
github.com/alexedwards/argon2id v0.0.0-20201228115903-cf543ebc1f7b
|
||||
github.com/aws/aws-sdk-go v1.37.18
|
||||
github.com/aws/aws-sdk-go v1.37.20
|
||||
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf // indirect
|
||||
github.com/eikenb/pipeat v0.0.0-20200430215831-470df5986b6d
|
||||
github.com/fclairamb/ftpserverlib v0.13.0
|
||||
github.com/frankban/quicktest v1.11.3 // indirect
|
||||
github.com/go-chi/chi v1.5.3
|
||||
github.com/go-chi/chi/v5 v5.0.0
|
||||
github.com/go-chi/jwtauth v1.2.0
|
||||
github.com/go-chi/render v1.0.1
|
||||
github.com/go-ole/go-ole v1.2.5 // indirect
|
||||
github.com/go-sql-driver/mysql v1.5.0
|
||||
github.com/golang/snappy v0.0.3 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||
github.com/google/uuid v1.2.0 // indirect
|
||||
github.com/google/wire v0.5.0 // indirect
|
||||
|
@ -26,11 +27,12 @@ require (
|
|||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.6.8
|
||||
github.com/jlaffaye/ftp v0.0.0-20201112195030-9aae4d151126
|
||||
github.com/lestrrat-go/backoff/v2 v2.0.8 // indirect
|
||||
github.com/lestrrat-go/jwx v1.1.4-0.20210228091017-d69abec6f5b4
|
||||
github.com/lib/pq v1.9.0
|
||||
github.com/magiconair/properties v1.8.4 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.6
|
||||
github.com/miekg/dns v1.1.39 // indirect
|
||||
github.com/miekg/dns v1.1.40 // indirect
|
||||
github.com/minio/sha256-simd v1.0.0
|
||||
github.com/minio/sio v0.2.1
|
||||
github.com/mitchellh/mapstructure v1.4.1 // indirect
|
||||
|
@ -39,7 +41,7 @@ require (
|
|||
github.com/pires/go-proxyproto v0.4.2
|
||||
github.com/pkg/sftp v1.12.1-0.20210222152308-b8102da57e75
|
||||
github.com/prometheus/client_golang v1.9.0
|
||||
github.com/prometheus/common v0.17.0 // indirect
|
||||
github.com/prometheus/common v0.18.0 // indirect
|
||||
github.com/prometheus/procfs v0.6.0 // indirect
|
||||
github.com/rs/cors v1.7.1-0.20200626170627-8b4a00bd362b
|
||||
github.com/rs/xid v1.2.1
|
||||
|
@ -62,10 +64,11 @@ require (
|
|||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83
|
||||
golang.org/x/net v0.0.0-20210224082022-3d97a244fca7
|
||||
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93 // indirect
|
||||
golang.org/x/sys v0.0.0-20210223212115-eede4237b368
|
||||
golang.org/x/sys v0.0.0-20210228012217-479acdf4ea46
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
|
||||
google.golang.org/api v0.40.0
|
||||
google.golang.org/genproto v0.0.0-20210224155714-063164c882e6 // indirect
|
||||
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705 // indirect
|
||||
google.golang.org/grpc v1.36.0 // indirect
|
||||
gopkg.in/ini.v1 v1.62.0 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
|
|
37
go.sum
37
go.sum
|
@ -77,7 +77,6 @@ github.com/Azure/go-autorest/autorest/azure/cli v0.4.2/go.mod h1:7qkJkT+j6b+hIpz
|
|||
github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw=
|
||||
github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
|
||||
github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE=
|
||||
github.com/Azure/go-autorest/autorest/validation v0.3.0/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E=
|
||||
|
@ -117,8 +116,8 @@ github.com/aws/aws-sdk-go v1.15.27/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZo
|
|||
github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go v1.36.1/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
|
||||
github.com/aws/aws-sdk-go v1.37.18 h1:SRdWLg+DqMFWX8HB3UvXyAoZpw9IDIUYnSTwgzOYbqg=
|
||||
github.com/aws/aws-sdk-go v1.37.18/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
|
||||
github.com/aws/aws-sdk-go v1.37.20 h1:CJCXpMYmBJrRH8YwoSE0oB9S3J5ax+62F14sYlDCztg=
|
||||
github.com/aws/aws-sdk-go v1.37.20/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
|
||||
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
|
@ -211,9 +210,10 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME
|
|||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do=
|
||||
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
||||
github.com/go-chi/chi v1.5.1 h1:kfTK3Cxd/dkMu/rKs5ZceWYp+t5CtiE7vmaTv3LjC6w=
|
||||
github.com/go-chi/chi v1.5.1/go.mod h1:REp24E+25iKvxgeTfHmdUoL5x15kBiDBlnIl5bCwe2k=
|
||||
github.com/go-chi/chi v1.5.3 h1:+DVDS9/D3MTbEu3WrrH3oz9oP6PlSPSNj8LLw3X17yU=
|
||||
github.com/go-chi/chi v1.5.3/go.mod h1:Q8xfe6s3fjZyMr8ZTv5jL+vxhVaFyCq2s+RvSfzTD0E=
|
||||
github.com/go-chi/chi/v5 v5.0.0 h1:DBPx88FjZJH3FsICfDAfIfnb7XxKIYVGG6lOPlhENAg=
|
||||
github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs=
|
||||
github.com/go-chi/jwtauth v1.2.0 h1:Z116SPpevIABBYsv8ih/AHYBHmd4EufKSKsLUnWdrTM=
|
||||
github.com/go-chi/jwtauth v1.2.0/go.mod h1:NTUpKoTQV6o25UwYE6w/VaLUu83hzrVKYTVo+lE6qDA=
|
||||
github.com/go-chi/render v1.0.1 h1:4/5tis2cKaNdnv9zFLfXzcquC9HbeZgCnxGnKrltBS8=
|
||||
|
@ -243,7 +243,6 @@ github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GO
|
|||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
|
||||
|
@ -288,8 +287,9 @@ github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM
|
|||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.2 h1:aeE13tS0IiQgFjYdoL8qN3K1N2bXXtI6Vi51/y7BpMw=
|
||||
github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA=
|
||||
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
|
@ -455,8 +455,9 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
|||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/lestrrat-go/backoff/v2 v2.0.7 h1:i2SeK33aOFJlUNJZzf2IpXRBvqBBnaGXfY5Xaop/GsE=
|
||||
github.com/lestrrat-go/backoff/v2 v2.0.7/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y=
|
||||
github.com/lestrrat-go/backoff/v2 v2.0.8 h1:oNb5E5isby2kiro9AgdHLv5N5tint1AnDVVf2E2un5A=
|
||||
github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y=
|
||||
github.com/lestrrat-go/codegen v1.0.0/go.mod h1:JhJw6OQAuPEfVKUCLItpaVLumDGWQznd1VaXrBk9TdM=
|
||||
github.com/lestrrat-go/httpcc v1.0.0 h1:FszVC6cKfDvBKcJv646+lkh4GydQg2Z29scgUfkOpYc=
|
||||
github.com/lestrrat-go/httpcc v1.0.0/go.mod h1:tGS/u00Vh5N6FHNkExqGGNId8e0Big+++0Gf8MBnAvE=
|
||||
|
@ -492,8 +493,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0j
|
|||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
github.com/miekg/dns v1.1.39 h1:6dRfDGnHiXOMmTZkwWANy7bBXXlKls5Qu+pn+Ue0TLo=
|
||||
github.com/miekg/dns v1.1.39/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
github.com/miekg/dns v1.1.40 h1:pyyPFfGMnciYUk/mXpKkVmeMQjfXqt3FAJ2hy7tPiLA=
|
||||
github.com/miekg/dns v1.1.40/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
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.2.1 h1:NjzKiIMSMcHediVQR0AFVx2tp7Wxh9tKPfDI3kH7aHQ=
|
||||
|
@ -549,7 +550,6 @@ github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnh
|
|||
github.com/otiai10/copy v1.5.0 h1:SoXDGnlTUZoqB/wSuj/Y5L6T5i6iN4YRAcMCd+JnLNU=
|
||||
github.com/otiai10/copy v1.5.0/go.mod h1:XWfuS3CrI0R6IE0FbgHsEazaXO8G0LpMp9o8tos0x4E=
|
||||
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
|
||||
github.com/otiai10/curr v1.0.0 h1:TJIWdbX0B+kpNagQrjgq8bCMrbhiuX73M2XwgtDMoOI=
|
||||
github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
|
||||
github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
|
||||
github.com/otiai10/mint v1.3.2 h1:VYWnrP5fXmz1MXvjuUvcBrXSjGE6xjON+axB/UrpO3E=
|
||||
|
@ -601,8 +601,8 @@ github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8
|
|||
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
|
||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||
github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
|
||||
github.com/prometheus/common v0.17.0 h1:kDIZLI74SS+3tedSvEkykgBkD7txMxaJAPj8DtJUKYA=
|
||||
github.com/prometheus/common v0.17.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
|
||||
github.com/prometheus/common v0.18.0 h1:WCVKW7aL6LEe1uryfI9dnEc2ZqNB1Fn0ok930v0iL1Y=
|
||||
github.com/prometheus/common v0.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
|
@ -842,8 +842,8 @@ golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210223212115-eede4237b368 h1:fDE3p0qf2V1co1vfj3/o87Ps8Hq6QTGNxJ5Xe7xSp80=
|
||||
golang.org/x/sys v0.0.0-20210223212115-eede4237b368/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210228012217-479acdf4ea46 h1:V066+OYJ66oTjnhm4Yrn7SXIwSCiDQJxpBxmvqb1N1c=
|
||||
golang.org/x/sys v0.0.0-20210228012217-479acdf4ea46/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
|
@ -1013,8 +1013,8 @@ google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6D
|
|||
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210203152818-3206188e46ba/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210224155714-063164c882e6 h1:bXUwz2WkXXrXgiLxww3vWmoSHLOGv4ipdPdTvKymcKw=
|
||||
google.golang.org/genproto v0.0.0-20210224155714-063164c882e6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705 h1:PYBmACG+YEv8uQPW0r1kJj8tR+gkF0UWq7iFdUezwEw=
|
||||
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/grpc v1.14.0/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=
|
||||
|
@ -1038,8 +1038,9 @@ google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM
|
|||
google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
|
||||
google.golang.org/grpc v1.35.0 h1:TwIQcH3es+MojMVojxxfQ3l3OF2KzlRxML2xZq0kRo8=
|
||||
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.36.0 h1:o1bcQ6imQMIOpdrO3SWf2z5RV72WbDwdXuK0MDlc8As=
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
|
|
|
@ -14,7 +14,7 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/jwtauth"
|
||||
|
||||
"github.com/drakkan/sftpgo/common"
|
||||
|
|
|
@ -20,7 +20,7 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/jwtauth"
|
||||
"github.com/lestrrat-go/jwx/jwt"
|
||||
"github.com/rs/xid"
|
||||
|
|
|
@ -1378,7 +1378,7 @@ components:
|
|||
enum:
|
||||
- None
|
||||
- CommonName
|
||||
description: defines the TLS certificate field to use as username. For FTP clients it must match the name provided using the "USER" command. Ignored if mutual TLS is disabled
|
||||
description: defines the TLS certificate field to use as username. For FTP clients it must match the name provided using the "USER" command. For WebDAV, if no username is provided, the CN will be used as username. For WebDAV clients it must match the implicit or provided username. Ignored if mutual TLS is disabled
|
||||
description: Additional user restrictions
|
||||
Secret:
|
||||
type: object
|
||||
|
|
|
@ -9,8 +9,8 @@ import (
|
|||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/go-chi/chi/middleware"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
"github.com/go-chi/jwtauth"
|
||||
"github.com/go-chi/render"
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import (
|
|||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi/middleware"
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
"github.com/rs/zerolog"
|
||||
|
||||
"github.com/drakkan/sftpgo/metrics"
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
package metrics
|
||||
|
||||
import (
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
package metrics
|
||||
|
||||
import (
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"github.com/drakkan/sftpgo/version"
|
||||
)
|
||||
|
|
|
@ -3,8 +3,8 @@ package telemetry
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/go-chi/chi/middleware"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
"github.com/go-chi/render"
|
||||
|
||||
"github.com/drakkan/sftpgo/common"
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"github.com/drakkan/sftpgo/common"
|
||||
"github.com/drakkan/sftpgo/logger"
|
||||
|
|
|
@ -398,7 +398,7 @@ func TestUserInvalidParams(t *testing.T) {
|
|||
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("/%v", u.Username), nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, err = server.validateUser(u, req)
|
||||
_, err = server.validateUser(u, req, dataprovider.LoginMethodPassword)
|
||||
if assert.Error(t, err) {
|
||||
assert.EqualError(t, err, fmt.Sprintf("cannot login user with invalid home dir: %#v", u.HomeDir))
|
||||
}
|
||||
|
@ -422,7 +422,7 @@ func TestUserInvalidParams(t *testing.T) {
|
|||
VirtualPath: vdirPath2,
|
||||
})
|
||||
|
||||
_, err = server.validateUser(u, req)
|
||||
_, err = server.validateUser(u, req, dataprovider.LoginMethodPassword)
|
||||
if assert.Error(t, err) {
|
||||
assert.EqualError(t, err, "overlapping mapped folders are allowed only with quota tracking disabled")
|
||||
}
|
||||
|
@ -923,14 +923,15 @@ func TestBasicUsersCache(t *testing.T) {
|
|||
|
||||
ipAddr := "127.0.0.1"
|
||||
|
||||
_, _, _, err = server.authenticate(req, ipAddr) //nolint:dogsled
|
||||
_, _, _, _, err = server.authenticate(req, ipAddr) //nolint:dogsled
|
||||
assert.Error(t, err)
|
||||
|
||||
now := time.Now()
|
||||
req.SetBasicAuth(username, password)
|
||||
_, isCached, _, err := server.authenticate(req, ipAddr)
|
||||
_, isCached, _, loginMethod, err := server.authenticate(req, ipAddr)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, isCached)
|
||||
assert.Equal(t, dataprovider.LoginMethodPassword, loginMethod)
|
||||
// now the user should be cached
|
||||
var cachedUser *dataprovider.CachedUser
|
||||
result, ok := dataprovider.GetCachedWebDAVUser(username)
|
||||
|
@ -939,14 +940,14 @@ func TestBasicUsersCache(t *testing.T) {
|
|||
assert.False(t, cachedUser.IsExpired())
|
||||
assert.True(t, cachedUser.Expiration.After(now.Add(time.Duration(c.Cache.Users.ExpirationTime)*time.Minute)))
|
||||
// authenticate must return the cached user now
|
||||
authUser, isCached, _, err := server.authenticate(req, ipAddr)
|
||||
authUser, isCached, _, _, err := server.authenticate(req, ipAddr)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, isCached)
|
||||
assert.Equal(t, cachedUser.User, authUser)
|
||||
}
|
||||
// a wrong password must fail
|
||||
req.SetBasicAuth(username, "wrong")
|
||||
_, _, _, err = server.authenticate(req, ipAddr) //nolint:dogsled
|
||||
_, _, _, _, err = server.authenticate(req, ipAddr) //nolint:dogsled
|
||||
assert.EqualError(t, err, dataprovider.ErrInvalidCredentials.Error())
|
||||
req.SetBasicAuth(username, password)
|
||||
|
||||
|
@ -959,9 +960,10 @@ func TestBasicUsersCache(t *testing.T) {
|
|||
assert.True(t, cachedUser.IsExpired())
|
||||
}
|
||||
// now authenticate should get the user from the data provider and update the cache
|
||||
_, isCached, _, err = server.authenticate(req, ipAddr)
|
||||
_, isCached, _, loginMethod, err = server.authenticate(req, ipAddr)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, isCached)
|
||||
assert.Equal(t, dataprovider.LoginMethodPassword, loginMethod)
|
||||
result, ok = dataprovider.GetCachedWebDAVUser(username)
|
||||
if assert.True(t, ok) {
|
||||
cachedUser = result.(*dataprovider.CachedUser)
|
||||
|
@ -973,9 +975,10 @@ func TestBasicUsersCache(t *testing.T) {
|
|||
_, ok = dataprovider.GetCachedWebDAVUser(username)
|
||||
assert.False(t, ok)
|
||||
|
||||
_, isCached, _, err = server.authenticate(req, ipAddr)
|
||||
_, isCached, _, loginMethod, err = server.authenticate(req, ipAddr)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, isCached)
|
||||
assert.Equal(t, dataprovider.LoginMethodPassword, loginMethod)
|
||||
_, ok = dataprovider.GetCachedWebDAVUser(username)
|
||||
assert.True(t, ok)
|
||||
// cache is invalidated after user deletion
|
||||
|
@ -1045,23 +1048,26 @@ func TestUsersCacheSizeAndExpiration(t *testing.T) {
|
|||
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("/%v", user1.Username), nil)
|
||||
assert.NoError(t, err)
|
||||
req.SetBasicAuth(user1.Username, password+"1")
|
||||
_, isCached, _, err := server.authenticate(req, ipAddr)
|
||||
_, isCached, _, loginMehod, err := server.authenticate(req, ipAddr)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, isCached)
|
||||
assert.Equal(t, dataprovider.LoginMethodPassword, loginMehod)
|
||||
|
||||
req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("/%v", user2.Username), nil)
|
||||
assert.NoError(t, err)
|
||||
req.SetBasicAuth(user2.Username, password+"2")
|
||||
_, isCached, _, err = server.authenticate(req, ipAddr)
|
||||
_, isCached, _, loginMehod, err = server.authenticate(req, ipAddr)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, isCached)
|
||||
assert.Equal(t, dataprovider.LoginMethodPassword, loginMehod)
|
||||
|
||||
req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("/%v", user3.Username), nil)
|
||||
assert.NoError(t, err)
|
||||
req.SetBasicAuth(user3.Username, password+"3")
|
||||
_, isCached, _, err = server.authenticate(req, ipAddr)
|
||||
_, isCached, _, loginMehod, err = server.authenticate(req, ipAddr)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, isCached)
|
||||
assert.Equal(t, dataprovider.LoginMethodPassword, loginMehod)
|
||||
|
||||
// the first 3 users are now cached
|
||||
_, ok := dataprovider.GetCachedWebDAVUser(user1.Username)
|
||||
|
@ -1074,9 +1080,10 @@ func TestUsersCacheSizeAndExpiration(t *testing.T) {
|
|||
req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("/%v", user4.Username), nil)
|
||||
assert.NoError(t, err)
|
||||
req.SetBasicAuth(user4.Username, password+"4")
|
||||
_, isCached, _, err = server.authenticate(req, ipAddr)
|
||||
_, isCached, _, loginMehod, err = server.authenticate(req, ipAddr)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, isCached)
|
||||
assert.Equal(t, dataprovider.LoginMethodPassword, loginMehod)
|
||||
// user1, the first cached, should be removed now
|
||||
_, ok = dataprovider.GetCachedWebDAVUser(user1.Username)
|
||||
assert.False(t, ok)
|
||||
|
@ -1091,9 +1098,10 @@ func TestUsersCacheSizeAndExpiration(t *testing.T) {
|
|||
req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("/%v", user1.Username), nil)
|
||||
assert.NoError(t, err)
|
||||
req.SetBasicAuth(user1.Username, password+"1")
|
||||
_, isCached, _, err = server.authenticate(req, ipAddr)
|
||||
_, isCached, _, loginMehod, err = server.authenticate(req, ipAddr)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, isCached)
|
||||
assert.Equal(t, dataprovider.LoginMethodPassword, loginMehod)
|
||||
_, ok = dataprovider.GetCachedWebDAVUser(user2.Username)
|
||||
assert.False(t, ok)
|
||||
_, ok = dataprovider.GetCachedWebDAVUser(user1.Username)
|
||||
|
@ -1107,9 +1115,10 @@ func TestUsersCacheSizeAndExpiration(t *testing.T) {
|
|||
req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("/%v", user2.Username), nil)
|
||||
assert.NoError(t, err)
|
||||
req.SetBasicAuth(user2.Username, password+"2")
|
||||
_, isCached, _, err = server.authenticate(req, ipAddr)
|
||||
_, isCached, _, loginMehod, err = server.authenticate(req, ipAddr)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, isCached)
|
||||
assert.Equal(t, dataprovider.LoginMethodPassword, loginMehod)
|
||||
_, ok = dataprovider.GetCachedWebDAVUser(user3.Username)
|
||||
assert.False(t, ok)
|
||||
_, ok = dataprovider.GetCachedWebDAVUser(user1.Username)
|
||||
|
@ -1123,9 +1132,10 @@ func TestUsersCacheSizeAndExpiration(t *testing.T) {
|
|||
req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("/%v", user3.Username), nil)
|
||||
assert.NoError(t, err)
|
||||
req.SetBasicAuth(user3.Username, password+"3")
|
||||
_, isCached, _, err = server.authenticate(req, ipAddr)
|
||||
_, isCached, _, loginMehod, err = server.authenticate(req, ipAddr)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, isCached)
|
||||
assert.Equal(t, dataprovider.LoginMethodPassword, loginMehod)
|
||||
_, ok = dataprovider.GetCachedWebDAVUser(user4.Username)
|
||||
assert.False(t, ok)
|
||||
_, ok = dataprovider.GetCachedWebDAVUser(user1.Username)
|
||||
|
@ -1144,16 +1154,18 @@ func TestUsersCacheSizeAndExpiration(t *testing.T) {
|
|||
req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("/%v", user4.Username), nil)
|
||||
assert.NoError(t, err)
|
||||
req.SetBasicAuth(user4.Username, password+"4")
|
||||
_, isCached, _, err = server.authenticate(req, ipAddr)
|
||||
_, isCached, _, loginMehod, err = server.authenticate(req, ipAddr)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, isCached)
|
||||
assert.Equal(t, dataprovider.LoginMethodPassword, loginMehod)
|
||||
|
||||
req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("/%v", user1.Username), nil)
|
||||
assert.NoError(t, err)
|
||||
req.SetBasicAuth(user1.Username, password+"1")
|
||||
_, isCached, _, err = server.authenticate(req, ipAddr)
|
||||
_, isCached, _, loginMehod, err = server.authenticate(req, ipAddr)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, isCached)
|
||||
assert.Equal(t, dataprovider.LoginMethodPassword, loginMehod)
|
||||
_, ok = dataprovider.GetCachedWebDAVUser(user2.Username)
|
||||
assert.False(t, ok)
|
||||
_, ok = dataprovider.GetCachedWebDAVUser(user1.Username)
|
||||
|
@ -1272,3 +1284,15 @@ func TestVerifyTLSConnection(t *testing.T) {
|
|||
|
||||
certMgr = oldCertMgr
|
||||
}
|
||||
|
||||
func TestMisc(t *testing.T) {
|
||||
oldCertMgr := certMgr
|
||||
|
||||
certMgr = nil
|
||||
err := ReloadCertificateMgr()
|
||||
assert.Nil(t, err)
|
||||
val := getConfigPath("", ".")
|
||||
assert.Empty(t, val)
|
||||
|
||||
certMgr = oldCertMgr
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi/middleware"
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
"github.com/rs/cors"
|
||||
"github.com/rs/xid"
|
||||
"golang.org/x/net/webdav"
|
||||
|
@ -69,10 +69,15 @@ func (s *webDavServer) listenAndServe(compressor *middleware.Compressor) error {
|
|||
}
|
||||
logger.Debug(logSender, "", "configured TLS cipher suites for binding %#v: %v", s.binding.GetAddress(),
|
||||
httpServer.TLSConfig.CipherSuites)
|
||||
if s.binding.ClientAuthType == 1 {
|
||||
if s.binding.isMutualTLSEnabled() {
|
||||
httpServer.TLSConfig.ClientCAs = certMgr.GetRootCAs()
|
||||
httpServer.TLSConfig.ClientAuth = tls.RequireAndVerifyClientCert
|
||||
httpServer.TLSConfig.VerifyConnection = s.verifyTLSConnection
|
||||
switch s.binding.ClientAuthType {
|
||||
case 1:
|
||||
httpServer.TLSConfig.ClientAuth = tls.RequireAndVerifyClientCert
|
||||
case 2:
|
||||
httpServer.TLSConfig.ClientAuth = tls.VerifyClientCertIfGiven
|
||||
}
|
||||
}
|
||||
logger.Info(logSender, "", "starting HTTPS serving, binding: %v", s.binding.GetAddress())
|
||||
return httpServer.ListenAndServeTLS("", "")
|
||||
|
@ -92,6 +97,9 @@ func (s *webDavServer) verifyTLSConnection(state tls.ConnectionState) error {
|
|||
clientCrtName = clientCrt.Subject.String()
|
||||
}
|
||||
if len(state.VerifiedChains) == 0 {
|
||||
if s.binding.ClientAuthType == 2 {
|
||||
return nil
|
||||
}
|
||||
logger.Warn(logSender, "", "TLS connection cannot be verified: unable to get verification chain")
|
||||
return errors.New("TLS connection cannot be verified: unable to get verification chain")
|
||||
}
|
||||
|
@ -152,28 +160,28 @@ func (s *webDavServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||
http.Error(w, common.ErrConnectionDenied.Error(), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
user, _, lockSystem, err := s.authenticate(r, ipAddr)
|
||||
user, _, lockSystem, loginMethod, err := s.authenticate(r, ipAddr)
|
||||
if err != nil {
|
||||
w.Header().Set("WWW-Authenticate", "Basic realm=\"SFTPGo WebDAV\"")
|
||||
http.Error(w, err401.Error(), http.StatusUnauthorized)
|
||||
http.Error(w, fmt.Sprintf("Authentication error: %v", err), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
connectionID, err := s.validateUser(&user, r)
|
||||
connectionID, err := s.validateUser(&user, r, loginMethod)
|
||||
if err != nil {
|
||||
updateLoginMetrics(&user, ipAddr, err)
|
||||
updateLoginMetrics(&user, ipAddr, loginMethod, err)
|
||||
http.Error(w, err.Error(), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
fs, err := user.GetFilesystem(connectionID)
|
||||
if err != nil {
|
||||
updateLoginMetrics(&user, ipAddr, err)
|
||||
updateLoginMetrics(&user, ipAddr, loginMethod, err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
updateLoginMetrics(&user, ipAddr, err)
|
||||
updateLoginMetrics(&user, ipAddr, loginMethod, err)
|
||||
|
||||
ctx := context.WithValue(r.Context(), requestIDKey, connectionID)
|
||||
ctx = context.WithValue(ctx, requestStartKey, time.Now())
|
||||
|
@ -202,12 +210,32 @@ func (s *webDavServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||
handler.ServeHTTP(w, r.WithContext(ctx))
|
||||
}
|
||||
|
||||
func (s *webDavServer) authenticate(r *http.Request, ip string) (dataprovider.User, bool, webdav.LockSystem, error) {
|
||||
func (s *webDavServer) getCredentialsAndLoginMethod(r *http.Request) (string, string, string, *x509.Certificate, bool) {
|
||||
var tlsCert *x509.Certificate
|
||||
loginMethod := dataprovider.LoginMethodPassword
|
||||
username, password, ok := r.BasicAuth()
|
||||
if s.binding.isMutualTLSEnabled() && r.TLS != nil {
|
||||
if len(r.TLS.PeerCertificates) > 0 {
|
||||
tlsCert = r.TLS.PeerCertificates[0]
|
||||
if ok {
|
||||
loginMethod = dataprovider.LoginMethodTLSCertificateAndPwd
|
||||
} else {
|
||||
loginMethod = dataprovider.LoginMethodTLSCertificate
|
||||
username = tlsCert.Subject.CommonName
|
||||
password = ""
|
||||
}
|
||||
ok = true
|
||||
}
|
||||
}
|
||||
return username, password, loginMethod, tlsCert, ok
|
||||
}
|
||||
|
||||
func (s *webDavServer) authenticate(r *http.Request, ip string) (dataprovider.User, bool, webdav.LockSystem, string, error) {
|
||||
var user dataprovider.User
|
||||
var err error
|
||||
username, password, ok := r.BasicAuth()
|
||||
username, password, loginMethod, tlsCert, ok := s.getCredentialsAndLoginMethod(r)
|
||||
if !ok {
|
||||
return user, false, nil, err401
|
||||
return user, false, nil, loginMethod, err401
|
||||
}
|
||||
result, ok := dataprovider.GetCachedWebDAVUser(username)
|
||||
if ok {
|
||||
|
@ -215,43 +243,47 @@ func (s *webDavServer) authenticate(r *http.Request, ip string) (dataprovider.Us
|
|||
if cachedUser.IsExpired() {
|
||||
dataprovider.RemoveCachedWebDAVUser(username)
|
||||
} else {
|
||||
if password != "" && cachedUser.Password == password {
|
||||
return cachedUser.User, true, cachedUser.LockSystem, nil
|
||||
if !cachedUser.User.IsTLSUsernameVerificationEnabled() {
|
||||
// for backward compatibility with 2.0.x we only check the password
|
||||
tlsCert = nil
|
||||
loginMethod = dataprovider.LoginMethodPassword
|
||||
}
|
||||
updateLoginMetrics(&cachedUser.User, ip, dataprovider.ErrInvalidCredentials)
|
||||
return user, false, nil, dataprovider.ErrInvalidCredentials
|
||||
if err := dataprovider.CheckCachedUserCredentials(cachedUser, password, loginMethod, common.ProtocolWebDAV, tlsCert); err == nil {
|
||||
return cachedUser.User, true, cachedUser.LockSystem, loginMethod, nil
|
||||
}
|
||||
updateLoginMetrics(&cachedUser.User, ip, loginMethod, dataprovider.ErrInvalidCredentials)
|
||||
return user, false, nil, loginMethod, dataprovider.ErrInvalidCredentials
|
||||
}
|
||||
}
|
||||
user, err = dataprovider.CheckUserAndPass(username, password, ip, common.ProtocolWebDAV)
|
||||
user, loginMethod, err = dataprovider.CheckCompositeCredentials(username, password, ip, loginMethod,
|
||||
common.ProtocolWebDAV, tlsCert)
|
||||
if err != nil {
|
||||
user.Username = username
|
||||
updateLoginMetrics(&user, ip, err)
|
||||
return user, false, nil, err
|
||||
updateLoginMetrics(&user, ip, loginMethod, err)
|
||||
return user, false, nil, loginMethod, err
|
||||
}
|
||||
lockSystem := webdav.NewMemLS()
|
||||
if password != "" {
|
||||
cachedUser := &dataprovider.CachedUser{
|
||||
User: user,
|
||||
Password: password,
|
||||
LockSystem: lockSystem,
|
||||
}
|
||||
if s.config.Cache.Users.ExpirationTime > 0 {
|
||||
cachedUser.Expiration = time.Now().Add(time.Duration(s.config.Cache.Users.ExpirationTime) * time.Minute)
|
||||
}
|
||||
dataprovider.CacheWebDAVUser(cachedUser, s.config.Cache.Users.MaxSize)
|
||||
if user.FsConfig.Provider != dataprovider.SFTPFilesystemProvider {
|
||||
// for sftp fs check root path does nothing so don't open a useless SFTP connection
|
||||
tempFs, err := user.GetFilesystem("temp")
|
||||
if err == nil {
|
||||
tempFs.CheckRootPath(user.Username, user.UID, user.GID)
|
||||
tempFs.Close()
|
||||
}
|
||||
cachedUser := &dataprovider.CachedUser{
|
||||
User: user,
|
||||
Password: password,
|
||||
LockSystem: lockSystem,
|
||||
}
|
||||
if s.config.Cache.Users.ExpirationTime > 0 {
|
||||
cachedUser.Expiration = time.Now().Add(time.Duration(s.config.Cache.Users.ExpirationTime) * time.Minute)
|
||||
}
|
||||
dataprovider.CacheWebDAVUser(cachedUser, s.config.Cache.Users.MaxSize)
|
||||
if user.FsConfig.Provider != dataprovider.SFTPFilesystemProvider {
|
||||
// for sftp fs check root path does nothing so don't open a useless SFTP connection
|
||||
tempFs, err := user.GetFilesystem("temp")
|
||||
if err == nil {
|
||||
tempFs.CheckRootPath(user.Username, user.UID, user.GID)
|
||||
tempFs.Close()
|
||||
}
|
||||
}
|
||||
return user, false, lockSystem, nil
|
||||
return user, false, lockSystem, loginMethod, nil
|
||||
}
|
||||
|
||||
func (s *webDavServer) validateUser(user *dataprovider.User, r *http.Request) (string, error) {
|
||||
func (s *webDavServer) validateUser(user *dataprovider.User, r *http.Request, loginMethod string) (string, error) {
|
||||
connID := xid.New().String()
|
||||
connectionID := fmt.Sprintf("%v_%v", common.ProtocolWebDAV, connID)
|
||||
|
||||
|
@ -264,9 +296,9 @@ func (s *webDavServer) validateUser(user *dataprovider.User, r *http.Request) (s
|
|||
logger.Debug(logSender, connectionID, "cannot login user %#v, protocol DAV is not allowed", user.Username)
|
||||
return connID, fmt.Errorf("Protocol DAV is not allowed for user %#v", user.Username)
|
||||
}
|
||||
if !user.IsLoginMethodAllowed(dataprovider.LoginMethodPassword, nil) {
|
||||
logger.Debug(logSender, connectionID, "cannot login user %#v, password login method is not allowed", user.Username)
|
||||
return connID, fmt.Errorf("Password login method is not allowed for user %#v", user.Username)
|
||||
if !user.IsLoginMethodAllowed(loginMethod, nil) {
|
||||
logger.Debug(logSender, connectionID, "cannot login user %#v, %v login method is not allowed", user.Username, loginMethod)
|
||||
return connID, fmt.Errorf("Login method %v is not allowed for user %#v", loginMethod, user.Username)
|
||||
}
|
||||
if user.MaxSessions > 0 {
|
||||
activeSessions := common.Connections.GetActiveSessions(user.Username)
|
||||
|
@ -335,16 +367,16 @@ func checkRemoteAddress(r *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
func updateLoginMetrics(user *dataprovider.User, ip string, err error) {
|
||||
metrics.AddLoginAttempt(dataprovider.LoginMethodPassword)
|
||||
func updateLoginMetrics(user *dataprovider.User, ip, loginMethod string, err error) {
|
||||
metrics.AddLoginAttempt(loginMethod)
|
||||
if err != nil {
|
||||
logger.ConnectionFailedLog(user.Username, ip, dataprovider.LoginMethodPassword, common.ProtocolWebDAV, err.Error())
|
||||
logger.ConnectionFailedLog(user.Username, ip, loginMethod, common.ProtocolWebDAV, err.Error())
|
||||
event := common.HostEventLoginFailed
|
||||
if _, ok := err.(*dataprovider.RecordNotFoundError); ok {
|
||||
event = common.HostEventUserNotFound
|
||||
}
|
||||
common.AddDefenderEvent(ip, event)
|
||||
}
|
||||
metrics.AddLoginResult(dataprovider.LoginMethodPassword, err)
|
||||
dataprovider.ExecutePostLoginHook(user, dataprovider.LoginMethodPassword, ip, common.ProtocolWebDAV, err)
|
||||
metrics.AddLoginResult(loginMethod, err)
|
||||
dataprovider.ExecutePostLoginHook(user, loginMethod, ip, common.ProtocolWebDAV, err)
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import (
|
|||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/go-chi/chi/middleware"
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
|
||||
"github.com/drakkan/sftpgo/common"
|
||||
"github.com/drakkan/sftpgo/logger"
|
||||
|
@ -88,6 +88,10 @@ type Binding struct {
|
|||
TLSCipherSuites []string `json:"tls_cipher_suites" mapstructure:"tls_cipher_suites"`
|
||||
}
|
||||
|
||||
func (b *Binding) isMutualTLSEnabled() bool {
|
||||
return b.ClientAuthType == 1 || b.ClientAuthType == 2
|
||||
}
|
||||
|
||||
// GetAddress returns the binding address
|
||||
func (b *Binding) GetAddress() string {
|
||||
return fmt.Sprintf("%s:%d", b.Address, b.Port)
|
||||
|
|
|
@ -3,6 +3,7 @@ package webdavd_test
|
|||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
|
@ -36,15 +37,17 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
logSender = "webavdTesting"
|
||||
webDavServerAddr = "127.0.0.1:9090"
|
||||
webDavServerPort = 9090
|
||||
sftpServerAddr = "127.0.0.1:9022"
|
||||
defaultUsername = "test_user_dav"
|
||||
defaultPassword = "test_password"
|
||||
configDir = ".."
|
||||
osWindows = "windows"
|
||||
webDavCert = `-----BEGIN CERTIFICATE-----
|
||||
logSender = "webavdTesting"
|
||||
webDavServerAddr = "localhost:9090"
|
||||
webDavTLSServerAddr = "localhost:9443"
|
||||
webDavServerPort = 9090
|
||||
webDavTLSServerPort = 9443
|
||||
sftpServerAddr = "127.0.0.1:9022"
|
||||
defaultUsername = "test_user_dav"
|
||||
defaultPassword = "test_password"
|
||||
configDir = ".."
|
||||
osWindows = "windows"
|
||||
webDavCert = `-----BEGIN CERTIFICATE-----
|
||||
MIICHTCCAaKgAwIBAgIUHnqw7QnB1Bj9oUsNpdb+ZkFPOxMwCgYIKoZIzj0EAwIw
|
||||
RTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGElu
|
||||
dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMDAyMDQwOTUzMDRaFw0zMDAyMDEw
|
||||
|
@ -67,8 +70,161 @@ UM2lmBLIXpGgBwYFK4EEACKhZANiAARCjRMqJ85rzMC998X5z761nJ+xL3bkmGVq
|
|||
WvrJ51t5OxV0v25NsOgR82CANXUgvhVYs7vNFN+jxtb2aj6Xg+/2G/BNxkaFspIV
|
||||
CzgWkxiz7XE4lgUwX44FCXZM3+JeUbI=
|
||||
-----END EC PRIVATE KEY-----`
|
||||
testFileName = "test_file_dav.dat"
|
||||
testDLFileName = "test_download_dav.dat"
|
||||
caCRT = `-----BEGIN CERTIFICATE-----
|
||||
MIIE5jCCAs6gAwIBAgIBATANBgkqhkiG9w0BAQsFADATMREwDwYDVQQDEwhDZXJ0
|
||||
QXV0aDAeFw0yMTAxMDIyMTIwNTVaFw0yMjA3MDIyMTMwNTJaMBMxETAPBgNVBAMT
|
||||
CENlcnRBdXRoMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA4Tiho5xW
|
||||
AC15JRkMwfp3/TJwI2As7MY5dele5cmdr5bHAE+sRKqC+Ti88OJWCV5saoyax/1S
|
||||
CjxJlQMZMl169P1QYJskKjdG2sdv6RLWLMgwSNRRjxp/Bw9dHdiEb9MjLgu28Jro
|
||||
9peQkHcRHeMf5hM9WvlIJGrdzbC4hUehmqggcqgARainBkYjf0SwuWxHeu4nMqkp
|
||||
Ak5tcSTLCjHfEFHZ9Te0TIPG5YkWocQKyeLgu4lvuU+DD2W2lym+YVUtRMGs1Env
|
||||
k7p+N0DcGU26qfzZ2sF5ZXkqm7dBsGQB9pIxwc2Q8T1dCIyP9OQCKVILdc5aVFf1
|
||||
cryQFHYzYNNZXFlIBims5VV5Mgfp8ESHQSue+v6n6ykecLEyKt1F1Y/MWY/nWUSI
|
||||
8zdq83jdBAZVjo9MSthxVn57/06s/hQca65IpcTZV2gX0a+eRlAVqaRbAhL3LaZe
|
||||
bYsW3WHKoUOftwemuep3nL51TzlXZVL7Oz/ClGaEOsnGG9KFO6jh+W768qC0zLQI
|
||||
CdE7v2Zex98sZteHCg9fGJHIaYoF0aJG5P3WI5oZf2fy7UIYN9ADLFZiorCXAZEh
|
||||
CSU6mDoRViZ4RGR9GZxbDZ9KYn7O8M/KCR72bkQg73TlMsk1zSXEw0MKLUjtsw6c
|
||||
rZ0Jt8t3sRatHO3JrYHALMt9vZfyNCZp0IsCAwEAAaNFMEMwDgYDVR0PAQH/BAQD
|
||||
AgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFO1yCNAGr/zQTJIi8lw3
|
||||
w5OiuBvMMA0GCSqGSIb3DQEBCwUAA4ICAQA6gCNuM7r8mnx674dm31GxBjQy5ZwB
|
||||
7CxDzYEvL/oiZ3Tv3HlPfN2LAAsJUfGnghh9DOytenL2CTZWjl/emP5eijzmlP+9
|
||||
zva5I6CIMCf/eDDVsRdO244t0o4uG7+At0IgSDM3bpVaVb4RHZNjEziYChsEYY8d
|
||||
HK6iwuRSvFniV6yhR/Vj1Ymi9yZ5xclqseLXiQnUB0PkfIk23+7s42cXB16653fH
|
||||
O/FsPyKBLiKJArizLYQc12aP3QOrYoYD9+fAzIIzew7A5C0aanZCGzkuFpO6TRlD
|
||||
Tb7ry9Gf0DfPpCgxraH8tOcmnqp/ka3hjqo/SRnnTk0IFrmmLdarJvjD46rKwBo4
|
||||
MjyAIR1mQ5j8GTlSFBmSgETOQ/EYvO3FPLmra1Fh7L+DvaVzTpqI9fG3TuyyY+Ri
|
||||
Fby4ycTOGSZOe5Fh8lqkX5Y47mCUJ3zHzOA1vUJy2eTlMRGpu47Eb1++Vm6EzPUP
|
||||
2EF5aD+zwcssh+atZvQbwxpgVqVcyLt91RSkKkmZQslh0rnlTb68yxvUnD3zw7So
|
||||
o6TAf9UvwVMEvdLT9NnFd6hwi2jcNte/h538GJwXeBb8EkfpqLKpTKyicnOdkamZ
|
||||
7E9zY8SHNRYMwB9coQ/W8NvufbCgkvOoLyMXk5edbXofXl3PhNGOlraWbghBnzf5
|
||||
r3rwjFsQOoZotA==
|
||||
-----END CERTIFICATE-----`
|
||||
caCRL = `-----BEGIN X509 CRL-----
|
||||
MIICpzCBkAIBATANBgkqhkiG9w0BAQsFADATMREwDwYDVQQDEwhDZXJ0QXV0aBcN
|
||||
MjEwMTAyMjEzNDA1WhcNMjMwMTAyMjEzNDA1WjAkMCICEQC+l04DbHWMyC3fG09k
|
||||
VXf+Fw0yMTAxMDIyMTM0MDVaoCMwITAfBgNVHSMEGDAWgBTtcgjQBq/80EySIvJc
|
||||
N8OTorgbzDANBgkqhkiG9w0BAQsFAAOCAgEAEJ7z+uNc8sqtxlOhSdTGDzX/xput
|
||||
E857kFQkSlMnU2whQ8c+XpYrBLA5vIZJNSSwohTpM4+zVBX/bJpmu3wqqaArRO9/
|
||||
YcW5mQk9Anvb4WjQW1cHmtNapMTzoC9AiYt/OWPfy+P6JCgCr4Hy6LgQyIRL6bM9
|
||||
VYTalolOm1qa4Y5cIeT7iHq/91mfaqo8/6MYRjLl8DOTROpmw8OS9bCXkzGKdCat
|
||||
AbAzwkQUSauyoCQ10rpX+Y64w9ng3g4Dr20aCqPf5osaqplEJ2HTK8ljDTidlslv
|
||||
9anQj8ax3Su89vI8+hK+YbfVQwrThabgdSjQsn+veyx8GlP8WwHLAQ379KjZjWg+
|
||||
OlOSwBeU1vTdP0QcB8X5C2gVujAyuQekbaV86xzIBOj7vZdfHZ6ee30TZ2FKiMyg
|
||||
7/N2OqW0w77ChsjB4MSHJCfuTgIeg62GzuZXLM+Q2Z9LBdtm4Byg+sm/P52adOEg
|
||||
gVb2Zf4KSvsAmA0PIBlu449/QXUFcMxzLFy7mwTeZj2B4Ln0Hm0szV9f9R8MwMtB
|
||||
SyLYxVH+mgqaR6Jkk22Q/yYyLPaELfafX5gp/AIXG8n0zxfVaTvK3auSgb1Q6ZLS
|
||||
5QH9dSIsmZHlPq7GoSXmKpMdjUL8eaky/IMteioyXgsBiATzl5L2dsw6MTX3MDF0
|
||||
QbDK+MzhmbKfDxs=
|
||||
-----END X509 CRL-----`
|
||||
client1Crt = `-----BEGIN CERTIFICATE-----
|
||||
MIIEITCCAgmgAwIBAgIRAIppZHoj1hM80D7WzTEKLuAwDQYJKoZIhvcNAQELBQAw
|
||||
EzERMA8GA1UEAxMIQ2VydEF1dGgwHhcNMjEwMTAyMjEyMzEwWhcNMjIwNzAyMjEz
|
||||
MDUxWjASMRAwDgYDVQQDEwdjbGllbnQxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
|
||||
MIIBCgKCAQEAoKbYY9MdF2kF/nhBESIiZTdVYtA8XL9xrIZyDj9EnCiTxHiVbJtH
|
||||
XVwszqSl5TRrotPmnmAQcX3r8OCk+z+RQZ0QQj257P3kG6q4rNnOcWCS5xEd20jP
|
||||
yhQ3m+hMGfZsotNTQze1ochuQgLUN6IPyPxZkH22ia3jX4iu1eo/QxeLYHj1UHw4
|
||||
3Cii9yE+j5kPUC21xmnrGKdUrB55NYLXHx6yTIqYR5znSOVB8oJi18/hwdZmH859
|
||||
DHhm0Hx1HrS+jbjI3+CMorZJ3WUyNf+CkiVLD3xYutPbxzEpwiqkG/XYzLH0habT
|
||||
cDcILo18n+o3jvem2KWBrDhyairjIDscwQIDAQABo3EwbzAOBgNVHQ8BAf8EBAMC
|
||||
A7gwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMB0GA1UdDgQWBBSJ5GIv
|
||||
zIrE4ZSQt2+CGblKTDswizAfBgNVHSMEGDAWgBTtcgjQBq/80EySIvJcN8OTorgb
|
||||
zDANBgkqhkiG9w0BAQsFAAOCAgEALh4f5GhvNYNou0Ab04iQBbLEdOu2RlbK1B5n
|
||||
K9P/umYenBHMY/z6HT3+6tpcHsDuqE8UVdq3f3Gh4S2Gu9m8PRitT+cJ3gdo9Plm
|
||||
3rD4ufn/s6rGg3ppydXcedm17492tbccUDWOBZw3IO/ASVq13WPgT0/Kev7cPq0k
|
||||
sSdSNhVeXqx8Myc2/d+8GYyzbul2Kpfa7h9i24sK49E9ftnSmsIvngONo08eT1T0
|
||||
3wAOyK2981LIsHaAWcneShKFLDB6LeXIT9oitOYhiykhFlBZ4M1GNlSNfhQ8IIQP
|
||||
xbqMNXCLkW4/BtLhGEEcg0QVso6Kudl9rzgTfQknrdF7pHp6rS46wYUjoSyIY6dl
|
||||
oLmnoAVJX36J3QPWelePI9e07X2wrTfiZWewwgw3KNRWjd6/zfPLe7GoqXnK1S2z
|
||||
PT8qMfCaTwKTtUkzXuTFvQ8bAo2My/mS8FOcpkt2oQWeOsADHAUX7fz5BCoa2DL3
|
||||
k/7Mh4gVT+JYZEoTwCFuYHgMWFWe98naqHi9lB4yR981p1QgXgxO7qBeipagKY1F
|
||||
LlH1iwXUqZ3MZnkNA+4e1Fglsw3sa/rC+L98HnznJ/YbTfQbCP6aQ1qcOymrjMud
|
||||
7MrFwqZjtd/SK4Qx1VpK6jGEAtPgWBTUS3p9ayg6lqjMBjsmySWfvRsDQbq6P5Ct
|
||||
O/e3EH8=
|
||||
-----END CERTIFICATE-----`
|
||||
client1Key = `-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEAoKbYY9MdF2kF/nhBESIiZTdVYtA8XL9xrIZyDj9EnCiTxHiV
|
||||
bJtHXVwszqSl5TRrotPmnmAQcX3r8OCk+z+RQZ0QQj257P3kG6q4rNnOcWCS5xEd
|
||||
20jPyhQ3m+hMGfZsotNTQze1ochuQgLUN6IPyPxZkH22ia3jX4iu1eo/QxeLYHj1
|
||||
UHw43Cii9yE+j5kPUC21xmnrGKdUrB55NYLXHx6yTIqYR5znSOVB8oJi18/hwdZm
|
||||
H859DHhm0Hx1HrS+jbjI3+CMorZJ3WUyNf+CkiVLD3xYutPbxzEpwiqkG/XYzLH0
|
||||
habTcDcILo18n+o3jvem2KWBrDhyairjIDscwQIDAQABAoIBAEBSjVFqtbsp0byR
|
||||
aXvyrtLX1Ng7h++at2jca85Ihq//jyqbHTje8zPuNAKI6eNbmb0YGr5OuEa4pD9N
|
||||
ssDmMsKSoG/lRwwcm7h4InkSvBWpFShvMgUaohfHAHzsBYxfnh+TfULsi0y7c2n6
|
||||
t/2OZcOTRkkUDIITnXYiw93ibHHv2Mv2bBDu35kGrcK+c2dN5IL5ZjTjMRpbJTe2
|
||||
44RBJbdTxHBVSgoGBnugF+s2aEma6Ehsj70oyfoVpM6Aed5kGge0A5zA1JO7WCn9
|
||||
Ay/DzlULRXHjJIoRWd2NKvx5n3FNppUc9vJh2plRHalRooZ2+MjSf8HmXlvG2Hpb
|
||||
ScvmWgECgYEA1G+A/2KnxWsr/7uWIJ7ClcGCiNLdk17Pv3DZ3G4qUsU2ITftfIbb
|
||||
tU0Q/b19na1IY8Pjy9ptP7t74/hF5kky97cf1FA8F+nMj/k4+wO8QDI8OJfzVzh9
|
||||
PwielA5vbE+xmvis5Hdp8/od1Yrc/rPSy2TKtPFhvsqXjqoUmOAjDP8CgYEAwZjH
|
||||
9dt1sc2lx/rMxihlWEzQ3JPswKW9/LJAmbRBoSWF9FGNjbX7uhWtXRKJkzb8ZAwa
|
||||
88azluNo2oftbDD/+jw8b2cDgaJHlLAkSD4O1D1RthW7/LKD15qZ/oFsRb13NV85
|
||||
ZNKtwslXGbfVNyGKUVFm7fVA8vBAOUey+LKDFj8CgYEAg8WWstOzVdYguMTXXuyb
|
||||
ruEV42FJaDyLiSirOvxq7GTAKuLSQUg1yMRBIeQEo2X1XU0JZE3dLodRVhuO4EXP
|
||||
g7Dn4X7Th9HSvgvNuIacowWGLWSz4Qp9RjhGhXhezUSx2nseY6le46PmFavJYYSR
|
||||
4PBofMyt4PcyA6Cknh+KHmkCgYEAnTriG7ETE0a7v4DXUpB4TpCEiMCy5Xs2o8Z5
|
||||
ZNva+W+qLVUWq+MDAIyechqeFSvxK6gRM69LJ96lx+XhU58wJiFJzAhT9rK/g+jS
|
||||
bsHH9WOfu0xHkuHA5hgvvV2Le9B2wqgFyva4HJy82qxMxCu/VG/SMqyfBS9OWbb7
|
||||
ibQhdq0CgYAl53LUWZsFSZIth1vux2LVOsI8C3X1oiXDGpnrdlQ+K7z57hq5EsRq
|
||||
GC+INxwXbvKNqp5h0z2MvmKYPDlGVTgw8f8JjM7TkN17ERLcydhdRrMONUryZpo8
|
||||
1xTob+8blyJgfxZUIAKbMbMbIiU0WAF0rfD/eJJwS4htOW/Hfv4TGA==
|
||||
-----END RSA PRIVATE KEY-----`
|
||||
// client 2 crt is revoked
|
||||
client2Crt = `-----BEGIN CERTIFICATE-----
|
||||
MIIEITCCAgmgAwIBAgIRAL6XTgNsdYzILd8bT2RVd/4wDQYJKoZIhvcNAQELBQAw
|
||||
EzERMA8GA1UEAxMIQ2VydEF1dGgwHhcNMjEwMTAyMjEyMzIwWhcNMjIwNzAyMjEz
|
||||
MDUxWjASMRAwDgYDVQQDEwdjbGllbnQyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
|
||||
MIIBCgKCAQEA6xjW5KQR3/OFQtV5M75WINqQ4AzXSu6DhSz/yumaaQZP/UxY+6hi
|
||||
jcrFzGo9MMie/Sza8DhkXOFAl2BelUubrOeB2cl+/Gr8OCyRi2Gv6j3zCsuN/4jQ
|
||||
tNaoez/IbkDvI3l/ZpzBtnuNY2RiemGgHuORXHRVf3qVlsw+npBIRW5rM2HkO/xG
|
||||
oZjeBErWVu390Lyn+Gvk2TqQDnkutWnxUC60/zPlHhXZ4BwaFAekbSnjsSDB1YFM
|
||||
s8HwW4oBryoxdj3/+/qLrBHt75IdLw3T7/V1UDJQM3EvSQOr12w4egpldhtsC871
|
||||
nnBQZeY6qA5feffIwwg/6lJm70o6S6OX6wIDAQABo3EwbzAOBgNVHQ8BAf8EBAMC
|
||||
A7gwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMB0GA1UdDgQWBBTB84v5
|
||||
t9HqhLhMODbn6oYkEQt3KzAfBgNVHSMEGDAWgBTtcgjQBq/80EySIvJcN8OTorgb
|
||||
zDANBgkqhkiG9w0BAQsFAAOCAgEALGtBCve5k8tToL3oLuXp/oSik6ovIB/zq4I/
|
||||
4zNMYPU31+ZWz6aahysgx1JL1yqTa3Qm8o2tu52MbnV10dM7CIw7c/cYa+c+OPcG
|
||||
5LF97kp13X+r2axy+CmwM86b4ILaDGs2Qyai6VB6k7oFUve+av5o7aUrNFpqGCJz
|
||||
HWdtHZSVA3JMATzy0TfWanwkzreqfdw7qH0yZ9bDURlBKAVWrqnCstva9jRuv+AI
|
||||
eqxr/4Ro986TFjJdoAP3Vr16CPg7/B6GA/KmsBWJrpeJdPWq4i2gpLKvYZoy89qD
|
||||
mUZf34RbzcCtV4NvV1DadGnt4us0nvLrvS5rL2+2uWD09kZYq9RbLkvgzF/cY0fz
|
||||
i7I1bi5XQ+alWe0uAk5ZZL/D+GTRYUX1AWwCqwJxmHrMxcskMyO9pXvLyuSWRDLo
|
||||
YNBrbX9nLcfJzVCp+X+9sntTHjs4l6Cw+fLepJIgtgqdCHtbhTiv68vSM6cgb4br
|
||||
6n2xrXRKuioiWFOrTSRr+oalZh8dGJ/xvwY8IbWknZAvml9mf1VvfE7Ma5P777QM
|
||||
fsbYVTq0Y3R/5hIWsC3HA5z6MIM8L1oRe/YyhP3CTmrCHkVKyDOosGXpGz+JVcyo
|
||||
cfYkY5A3yFKB2HaCwZSfwFmRhxkrYWGEbHv3Cd9YkZs1J3hNhGFZyVMC9Uh0S85a
|
||||
6zdDidU=
|
||||
-----END CERTIFICATE-----`
|
||||
client2Key = `-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEA6xjW5KQR3/OFQtV5M75WINqQ4AzXSu6DhSz/yumaaQZP/UxY
|
||||
+6hijcrFzGo9MMie/Sza8DhkXOFAl2BelUubrOeB2cl+/Gr8OCyRi2Gv6j3zCsuN
|
||||
/4jQtNaoez/IbkDvI3l/ZpzBtnuNY2RiemGgHuORXHRVf3qVlsw+npBIRW5rM2Hk
|
||||
O/xGoZjeBErWVu390Lyn+Gvk2TqQDnkutWnxUC60/zPlHhXZ4BwaFAekbSnjsSDB
|
||||
1YFMs8HwW4oBryoxdj3/+/qLrBHt75IdLw3T7/V1UDJQM3EvSQOr12w4egpldhts
|
||||
C871nnBQZeY6qA5feffIwwg/6lJm70o6S6OX6wIDAQABAoIBAFatstVb1KdQXsq0
|
||||
cFpui8zTKOUiduJOrDkWzTygAmlEhYtrccdfXu7OWz0x0lvBLDVGK3a0I/TGrAzj
|
||||
4BuFY+FM/egxTVt9in6fmA3et4BS1OAfCryzUdfK6RV//8L+t+zJZ/qKQzWnugpy
|
||||
QYjDo8ifuMFwtvEoXizaIyBNLAhEp9hnrv+Tyi2O2gahPvCHsD48zkyZRCHYRstD
|
||||
NH5cIrwz9/RJgPO1KI+QsJE7Nh7stR0sbr+5TPU4fnsL2mNhMUF2TJrwIPrc1yp+
|
||||
YIUjdnh3SO88j4TQT3CIrWi8i4pOy6N0dcVn3gpCRGaqAKyS2ZYUj+yVtLO4KwxZ
|
||||
SZ1lNvECgYEA78BrF7f4ETfWSLcBQ3qxfLs7ibB6IYo2x25685FhZjD+zLXM1AKb
|
||||
FJHEXUm3mUYrFJK6AFEyOQnyGKBOLs3S6oTAswMPbTkkZeD1Y9O6uv0AHASLZnK6
|
||||
pC6ub0eSRF5LUyTQ55Jj8D7QsjXJueO8v+G5ihWhNSN9tB2UA+8NBmkCgYEA+weq
|
||||
cvoeMIEMBQHnNNLy35bwfqrceGyPIRBcUIvzQfY1vk7KW6DYOUzC7u+WUzy/hA52
|
||||
DjXVVhua2eMQ9qqtOav7djcMc2W9RbLowxvno7K5qiCss013MeWk64TCWy+WMp5A
|
||||
AVAtOliC3hMkIKqvR2poqn+IBTh1449agUJQqTMCgYEAu06IHGq1GraV6g9XpGF5
|
||||
wqoAlMzUTdnOfDabRilBf/YtSr+J++ThRcuwLvXFw7CnPZZ4TIEjDJ7xjj3HdxeE
|
||||
fYYjineMmNd40UNUU556F1ZLvJfsVKizmkuCKhwvcMx+asGrmA+tlmds4p3VMS50
|
||||
KzDtpKzLWlmU/p/RINWlRmkCgYBy0pHTn7aZZx2xWKqCDg+L2EXPGqZX6wgZDpu7
|
||||
OBifzlfM4ctL2CmvI/5yPmLbVgkgBWFYpKUdiujsyyEiQvWTUKhn7UwjqKDHtcsk
|
||||
G6p7xS+JswJrzX4885bZJ9Oi1AR2yM3sC9l0O7I4lDbNPmWIXBLeEhGMmcPKv/Kc
|
||||
91Ff4wKBgQCF3ur+Vt0PSU0ucrPVHjCe7tqazm0LJaWbPXL1Aw0pzdM2EcNcW/MA
|
||||
w0kqpr7MgJ94qhXCBcVcfPuFN9fBOadM3UBj1B45Cz3pptoK+ScI8XKno6jvVK/p
|
||||
xr5cb9VBRBtB9aOKVfuRhpatAfS2Pzm2Htae9lFn7slGPUmu2hkjDw==
|
||||
-----END RSA PRIVATE KEY-----`
|
||||
testFileName = "test_file_dav.dat"
|
||||
testDLFileName = "test_download_dav.dat"
|
||||
tlsClient1Username = "client1"
|
||||
tlsClient2Username = "client2"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -81,6 +237,8 @@ var (
|
|||
logFilePath string
|
||||
certPath string
|
||||
keyPath string
|
||||
caCrtPath string
|
||||
caCRLPath string
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
|
@ -108,6 +266,8 @@ func TestMain(m *testing.M) {
|
|||
|
||||
certPath = filepath.Join(os.TempDir(), "test_dav.crt")
|
||||
keyPath = filepath.Join(os.TempDir(), "test_dav.key")
|
||||
caCrtPath = filepath.Join(os.TempDir(), "test_dav_ca.crt")
|
||||
caCRLPath = filepath.Join(os.TempDir(), "test_dav_crl.crt")
|
||||
err = os.WriteFile(certPath, []byte(webDavCert), os.ModePerm)
|
||||
if err != nil {
|
||||
logger.ErrorToConsole("error writing WebDAV certificate: %v", err)
|
||||
|
@ -118,6 +278,16 @@ func TestMain(m *testing.M) {
|
|||
logger.ErrorToConsole("error writing WebDAV private key: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
err = os.WriteFile(caCrtPath, []byte(caCRT), os.ModePerm)
|
||||
if err != nil {
|
||||
logger.ErrorToConsole("error writing WebDAV CA crt: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
err = os.WriteFile(caCRLPath, []byte(caCRL), os.ModePerm)
|
||||
if err != nil {
|
||||
logger.ErrorToConsole("error writing WebDAV CRL: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
err = common.Initialize(commonConf)
|
||||
if err != nil {
|
||||
|
@ -155,10 +325,19 @@ func TestMain(m *testing.M) {
|
|||
sftpdConf.HostKeys = []string{hostKeyPath}
|
||||
|
||||
webDavConf := config.GetWebDAVDConfig()
|
||||
webDavConf.CertificateFile = certPath
|
||||
webDavConf.CertificateKeyFile = keyPath
|
||||
webDavConf.CACertificates = []string{caCrtPath}
|
||||
webDavConf.CARevocationLists = []string{caCRLPath}
|
||||
webDavConf.Bindings = []webdavd.Binding{
|
||||
{
|
||||
Port: webDavServerPort,
|
||||
},
|
||||
{
|
||||
Port: webDavTLSServerPort,
|
||||
EnableHTTPS: true,
|
||||
ClientAuthType: 2,
|
||||
},
|
||||
}
|
||||
webDavConf.Cors = webdavd.Cors{
|
||||
Enabled: true,
|
||||
|
@ -209,6 +388,7 @@ func TestMain(m *testing.M) {
|
|||
}()
|
||||
|
||||
waitTCPListening(webDavConf.Bindings[0].GetAddress())
|
||||
waitTCPListening(webDavConf.Bindings[1].GetAddress())
|
||||
waitTCPListening(httpdConf.Bindings[0].GetAddress())
|
||||
waitTCPListening(sftpdConf.Bindings[0].GetAddress())
|
||||
webdavd.ReloadCertificateMgr() //nolint:errcheck
|
||||
|
@ -220,6 +400,8 @@ func TestMain(m *testing.M) {
|
|||
os.Remove(postConnectPath)
|
||||
os.Remove(certPath)
|
||||
os.Remove(keyPath)
|
||||
os.Remove(caCrtPath)
|
||||
os.Remove(caCRLPath)
|
||||
os.Remove(hostKeyPath)
|
||||
os.Remove(hostKeyPath + ".pub")
|
||||
os.Exit(exitCode)
|
||||
|
@ -281,6 +463,13 @@ func TestInitialization(t *testing.T) {
|
|||
cfg.CARevocationLists = nil
|
||||
err = cfg.Initialize(configDir)
|
||||
assert.Error(t, err)
|
||||
|
||||
cfg.CertificateFile = certPath
|
||||
cfg.CertificateKeyFile = keyPath
|
||||
cfg.CACertificates = []string{caCrtPath}
|
||||
cfg.CARevocationLists = []string{caCRLPath}
|
||||
err = cfg.Initialize(configDir)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestBasicHandling(t *testing.T) {
|
||||
|
@ -293,7 +482,7 @@ func TestBasicHandling(t *testing.T) {
|
|||
sftpUser, _, err := httpdtest.AddUser(u, http.StatusCreated)
|
||||
assert.NoError(t, err)
|
||||
for _, user := range []dataprovider.User{localUser, sftpUser} {
|
||||
client := getWebDavClient(user)
|
||||
client := getWebDavClient(user, true, nil)
|
||||
assert.NoError(t, checkBasicFunc(client))
|
||||
testFilePath := filepath.Join(homeBasePath, testFileName)
|
||||
testFileSize := int64(65535)
|
||||
|
@ -378,7 +567,7 @@ func TestBasicHandlingCryptFs(t *testing.T) {
|
|||
u.QuotaSize = 6553600
|
||||
user, _, err := httpdtest.AddUser(u, http.StatusCreated)
|
||||
assert.NoError(t, err)
|
||||
client := getWebDavClient(user)
|
||||
client := getWebDavClient(user, false, nil)
|
||||
assert.NoError(t, checkBasicFunc(client))
|
||||
|
||||
testFilePath := filepath.Join(homeBasePath, testFileName)
|
||||
|
@ -459,7 +648,7 @@ func TestPropPatch(t *testing.T) {
|
|||
for _, u := range []dataprovider.User{getTestUser(), getTestUserWithCryptFs(), sftpUser} {
|
||||
user, _, err := httpdtest.AddUser(u, http.StatusCreated)
|
||||
assert.NoError(t, err)
|
||||
client := getWebDavClient(user)
|
||||
client := getWebDavClient(user, true, nil)
|
||||
assert.NoError(t, checkBasicFunc(client), sftpUser.Username)
|
||||
|
||||
testFilePath := filepath.Join(homeBasePath, testFileName)
|
||||
|
@ -500,10 +689,10 @@ func TestLoginInvalidPwd(t *testing.T) {
|
|||
u := getTestUser()
|
||||
user, _, err := httpdtest.AddUser(u, http.StatusCreated)
|
||||
assert.NoError(t, err)
|
||||
client := getWebDavClient(user)
|
||||
client := getWebDavClient(user, false, nil)
|
||||
assert.NoError(t, checkBasicFunc(client))
|
||||
user.Password = "wrong"
|
||||
client = getWebDavClient(user)
|
||||
client = getWebDavClient(user, false, nil)
|
||||
assert.Error(t, checkBasicFunc(client))
|
||||
_, err = httpdtest.RemoveUser(user, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
|
@ -511,7 +700,7 @@ func TestLoginInvalidPwd(t *testing.T) {
|
|||
|
||||
func TestLoginNonExistentUser(t *testing.T) {
|
||||
user := getTestUser()
|
||||
client := getWebDavClient(user)
|
||||
client := getWebDavClient(user, true, nil)
|
||||
assert.Error(t, checkBasicFunc(client))
|
||||
}
|
||||
|
||||
|
@ -527,17 +716,17 @@ func TestDefender(t *testing.T) {
|
|||
|
||||
user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
|
||||
assert.NoError(t, err)
|
||||
client := getWebDavClient(user)
|
||||
client := getWebDavClient(user, true, nil)
|
||||
assert.NoError(t, checkBasicFunc(client))
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
user.Password = "wrong_pwd"
|
||||
client = getWebDavClient(user)
|
||||
client = getWebDavClient(user, false, nil)
|
||||
assert.Error(t, checkBasicFunc(client))
|
||||
}
|
||||
|
||||
user.Password = defaultPassword
|
||||
client = getWebDavClient(user)
|
||||
client = getWebDavClient(user, true, nil)
|
||||
err = checkBasicFunc(client)
|
||||
if assert.Error(t, err) {
|
||||
assert.Contains(t, err.Error(), "403")
|
||||
|
@ -568,10 +757,10 @@ func TestLoginExternalAuth(t *testing.T) {
|
|||
providerConf.ExternalAuthScope = 0
|
||||
err = dataprovider.Initialize(providerConf, configDir, true)
|
||||
assert.NoError(t, err)
|
||||
client := getWebDavClient(u)
|
||||
client := getWebDavClient(u, false, nil)
|
||||
assert.NoError(t, checkBasicFunc(client))
|
||||
u.Username = defaultUsername + "1"
|
||||
client = getWebDavClient(u)
|
||||
client = getWebDavClient(u, false, nil)
|
||||
assert.Error(t, checkBasicFunc(client))
|
||||
user, _, err := httpdtest.GetUserByUsername(defaultUsername, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
|
@ -608,20 +797,20 @@ func TestPreLoginHook(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
_, _, err = httpdtest.GetUserByUsername(defaultUsername, http.StatusNotFound)
|
||||
assert.NoError(t, err)
|
||||
client := getWebDavClient(u)
|
||||
client := getWebDavClient(u, true, nil)
|
||||
assert.NoError(t, checkBasicFunc(client))
|
||||
|
||||
user, _, err := httpdtest.GetUserByUsername(defaultUsername, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
// test login with an existing user
|
||||
client = getWebDavClient(user)
|
||||
client = getWebDavClient(user, true, nil)
|
||||
assert.NoError(t, checkBasicFunc(client))
|
||||
err = os.WriteFile(preLoginPath, getPreLoginScriptContent(user, true), os.ModePerm)
|
||||
assert.NoError(t, err)
|
||||
// update the user to remove it from the cache
|
||||
user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
|
||||
assert.NoError(t, err)
|
||||
client = getWebDavClient(user)
|
||||
client = getWebDavClient(user, true, nil)
|
||||
assert.Error(t, checkBasicFunc(client))
|
||||
// update the user to remove it from the cache
|
||||
user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
|
||||
|
@ -629,7 +818,7 @@ func TestPreLoginHook(t *testing.T) {
|
|||
user.Status = 0
|
||||
err = os.WriteFile(preLoginPath, getPreLoginScriptContent(user, false), os.ModePerm)
|
||||
assert.NoError(t, err)
|
||||
client = getWebDavClient(user)
|
||||
client = getWebDavClient(user, true, nil)
|
||||
assert.Error(t, checkBasicFunc(client))
|
||||
|
||||
_, err = httpdtest.RemoveUser(user, http.StatusOK)
|
||||
|
@ -658,7 +847,7 @@ func TestPostConnectHook(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
err = os.WriteFile(postConnectPath, getPostConnectScriptContent(0), os.ModePerm)
|
||||
assert.NoError(t, err)
|
||||
client := getWebDavClient(user)
|
||||
client := getWebDavClient(user, false, nil)
|
||||
assert.NoError(t, checkBasicFunc(client))
|
||||
err = os.WriteFile(postConnectPath, getPostConnectScriptContent(1), os.ModePerm)
|
||||
assert.NoError(t, err)
|
||||
|
@ -684,7 +873,7 @@ func TestMaxConnections(t *testing.T) {
|
|||
|
||||
user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
|
||||
assert.NoError(t, err)
|
||||
client := getWebDavClient(user)
|
||||
client := getWebDavClient(user, true, nil)
|
||||
assert.NoError(t, checkBasicFunc(client))
|
||||
// now add a fake connection
|
||||
fs := vfs.NewOsFs("id", os.TempDir(), nil)
|
||||
|
@ -708,7 +897,7 @@ func TestMaxSessions(t *testing.T) {
|
|||
u.MaxSessions = 1
|
||||
user, _, err := httpdtest.AddUser(u, http.StatusCreated)
|
||||
assert.NoError(t, err)
|
||||
client := getWebDavClient(user)
|
||||
client := getWebDavClient(user, false, nil)
|
||||
assert.NoError(t, checkBasicFunc(client))
|
||||
// now add a fake connection
|
||||
fs := vfs.NewOsFs("id", os.TempDir(), nil)
|
||||
|
@ -731,7 +920,7 @@ func TestLoginWithIPilters(t *testing.T) {
|
|||
u.Filters.AllowedIP = []string{"172.19.0.0/16"}
|
||||
user, _, err := httpdtest.AddUser(u, http.StatusCreated)
|
||||
assert.NoError(t, err)
|
||||
client := getWebDavClient(user)
|
||||
client := getWebDavClient(user, true, nil)
|
||||
assert.Error(t, checkBasicFunc(client))
|
||||
|
||||
_, err = httpdtest.RemoveUser(user, http.StatusOK)
|
||||
|
@ -765,7 +954,7 @@ func TestDownloadErrors(t *testing.T) {
|
|||
}
|
||||
user, _, err := httpdtest.AddUser(u, http.StatusCreated)
|
||||
assert.NoError(t, err)
|
||||
client := getWebDavClient(user)
|
||||
client := getWebDavClient(user, false, nil)
|
||||
testFilePath1 := filepath.Join(user.HomeDir, subDir1, "file.zipp")
|
||||
testFilePath2 := filepath.Join(user.HomeDir, subDir2, "file.zipp")
|
||||
testFilePath3 := filepath.Join(user.HomeDir, subDir2, "file.jpg")
|
||||
|
@ -815,7 +1004,7 @@ func TestUploadErrors(t *testing.T) {
|
|||
}
|
||||
user, _, err := httpdtest.AddUser(u, http.StatusCreated)
|
||||
assert.NoError(t, err)
|
||||
client := getWebDavClient(user)
|
||||
client := getWebDavClient(user, true, nil)
|
||||
testFilePath := filepath.Join(homeBasePath, testFileName)
|
||||
testFileSize := user.QuotaSize
|
||||
err = createTestFile(testFilePath, testFileSize)
|
||||
|
@ -859,13 +1048,13 @@ func TestDeniedLoginMethod(t *testing.T) {
|
|||
u.Filters.DeniedLoginMethods = []string{dataprovider.LoginMethodPassword}
|
||||
user, _, err := httpdtest.AddUser(u, http.StatusCreated)
|
||||
assert.NoError(t, err)
|
||||
client := getWebDavClient(user)
|
||||
client := getWebDavClient(user, true, nil)
|
||||
assert.Error(t, checkBasicFunc(client))
|
||||
|
||||
user.Filters.DeniedLoginMethods = []string{dataprovider.SSHLoginMethodPublicKey, dataprovider.SSHLoginMethodKeyAndKeyboardInt}
|
||||
user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
|
||||
assert.NoError(t, err)
|
||||
client = getWebDavClient(user)
|
||||
client = getWebDavClient(user, true, nil)
|
||||
assert.NoError(t, checkBasicFunc(client))
|
||||
|
||||
_, err = httpdtest.RemoveUser(user, http.StatusOK)
|
||||
|
@ -879,13 +1068,13 @@ func TestDeniedProtocols(t *testing.T) {
|
|||
u.Filters.DeniedProtocols = []string{common.ProtocolWebDAV}
|
||||
user, _, err := httpdtest.AddUser(u, http.StatusCreated)
|
||||
assert.NoError(t, err)
|
||||
client := getWebDavClient(user)
|
||||
client := getWebDavClient(user, false, nil)
|
||||
assert.Error(t, checkBasicFunc(client))
|
||||
|
||||
user.Filters.DeniedProtocols = []string{common.ProtocolSSH, common.ProtocolFTP}
|
||||
user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
|
||||
assert.NoError(t, err)
|
||||
client = getWebDavClient(user)
|
||||
client = getWebDavClient(user, false, nil)
|
||||
assert.NoError(t, checkBasicFunc(client))
|
||||
|
||||
_, err = httpdtest.RemoveUser(user, http.StatusOK)
|
||||
|
@ -918,7 +1107,7 @@ func TestQuotaLimits(t *testing.T) {
|
|||
testFilePath2 := filepath.Join(homeBasePath, testFileName2)
|
||||
err = createTestFile(testFilePath2, testFileSize2)
|
||||
assert.NoError(t, err)
|
||||
client := getWebDavClient(user)
|
||||
client := getWebDavClient(user, true, nil)
|
||||
// test quota files
|
||||
err = uploadFile(testFilePath, testFileName+".quota", testFileSize, client)
|
||||
assert.NoError(t, err)
|
||||
|
@ -1001,7 +1190,7 @@ func TestUploadMaxSize(t *testing.T) {
|
|||
testFilePath1 := filepath.Join(homeBasePath, testFileName1)
|
||||
err = createTestFile(testFilePath1, testFileSize1)
|
||||
assert.NoError(t, err)
|
||||
client := getWebDavClient(user)
|
||||
client := getWebDavClient(user, false, nil)
|
||||
err = uploadFile(testFilePath1, testFileName1, testFileSize1, client)
|
||||
assert.Error(t, err)
|
||||
err = uploadFile(testFilePath, testFileName, testFileSize, client)
|
||||
|
@ -1048,7 +1237,7 @@ func TestClientClose(t *testing.T) {
|
|||
testFilePath := filepath.Join(homeBasePath, testFileName)
|
||||
err = createTestFile(testFilePath, testFileSize)
|
||||
assert.NoError(t, err)
|
||||
client := getWebDavClient(user)
|
||||
client := getWebDavClient(user, true, nil)
|
||||
assert.NoError(t, checkBasicFunc(client))
|
||||
|
||||
var wg sync.WaitGroup
|
||||
|
@ -1149,7 +1338,7 @@ func TestLoginWithDatabaseCredentials(t *testing.T) {
|
|||
|
||||
assert.NoFileExists(t, credentialsFile)
|
||||
|
||||
client := getWebDavClient(user)
|
||||
client := getWebDavClient(user, false, nil)
|
||||
|
||||
err = client.Connect()
|
||||
assert.NoError(t, err)
|
||||
|
@ -1183,7 +1372,7 @@ func TestLoginInvalidFs(t *testing.T) {
|
|||
err = os.Remove(credentialsFile)
|
||||
assert.NoError(t, err)
|
||||
|
||||
client := getWebDavClient(user)
|
||||
client := getWebDavClient(user, true, nil)
|
||||
assert.Error(t, checkBasicFunc(client))
|
||||
|
||||
_, err = httpdtest.RemoveUser(user, http.StatusOK)
|
||||
|
@ -1208,7 +1397,7 @@ func TestBytesRangeRequests(t *testing.T) {
|
|||
fileContent := []byte("test file contents")
|
||||
err = os.WriteFile(testFilePath, fileContent, os.ModePerm)
|
||||
assert.NoError(t, err)
|
||||
client := getWebDavClient(user)
|
||||
client := getWebDavClient(user, true, nil)
|
||||
err = uploadFile(testFilePath, testFileName, int64(len(fileContent)), client)
|
||||
assert.NoError(t, err)
|
||||
remotePath := fmt.Sprintf("http://%v/%v", webDavServerAddr, testFileName)
|
||||
|
@ -1290,7 +1479,7 @@ func TestGETAsPROPFIND(t *testing.T) {
|
|||
resp.Body.Close()
|
||||
}
|
||||
}
|
||||
client := getWebDavClient(user)
|
||||
client := getWebDavClient(user, false, nil)
|
||||
err = client.MkdirAll(path.Join(subDir1, "sub", "sub1"), os.ModePerm)
|
||||
assert.NoError(t, err)
|
||||
subPath := fmt.Sprintf("http://%v/%v", webDavServerAddr, subDir1)
|
||||
|
@ -1341,7 +1530,7 @@ func TestStat(t *testing.T) {
|
|||
u.Permissions["/subdir"] = []string{dataprovider.PermUpload, dataprovider.PermListItems, dataprovider.PermDownload}
|
||||
user, _, err := httpdtest.AddUser(u, http.StatusCreated)
|
||||
assert.NoError(t, err)
|
||||
client := getWebDavClient(user)
|
||||
client := getWebDavClient(user, true, nil)
|
||||
subDir := "subdir"
|
||||
testFilePath := filepath.Join(homeBasePath, testFileName)
|
||||
testFileSize := int64(65535)
|
||||
|
@ -1387,7 +1576,7 @@ func TestUploadOverwriteVfolder(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
user, _, err := httpdtest.AddUser(u, http.StatusCreated)
|
||||
assert.NoError(t, err)
|
||||
client := getWebDavClient(user)
|
||||
client := getWebDavClient(user, false, nil)
|
||||
files, err := client.ReadDir(".")
|
||||
assert.NoError(t, err)
|
||||
vdirFound := false
|
||||
|
@ -1442,7 +1631,7 @@ func TestMiscCommands(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
for _, user := range []dataprovider.User{localUser, sftpUser} {
|
||||
dir := "testDir"
|
||||
client := getWebDavClient(user)
|
||||
client := getWebDavClient(user, true, nil)
|
||||
err = client.MkdirAll(path.Join(dir, "sub1", "sub2"), os.ModePerm)
|
||||
assert.NoError(t, err)
|
||||
testFilePath := filepath.Join(homeBasePath, testFileName)
|
||||
|
@ -1504,6 +1693,319 @@ func TestMiscCommands(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestClientCertificateAuthRevokedCert(t *testing.T) {
|
||||
u := getTestUser()
|
||||
u.Username = tlsClient2Username
|
||||
u.Filters.TLSUsername = dataprovider.TLSUsernameCN
|
||||
user, _, err := httpdtest.AddUser(u, http.StatusCreated)
|
||||
assert.NoError(t, err)
|
||||
tlsConfig := &tls.Config{
|
||||
ServerName: "localhost",
|
||||
InsecureSkipVerify: true, // use this for tests only
|
||||
MinVersion: tls.VersionTLS12,
|
||||
}
|
||||
tlsCert, err := tls.X509KeyPair([]byte(client2Crt), []byte(client2Key))
|
||||
assert.NoError(t, err)
|
||||
tlsConfig.Certificates = append(tlsConfig.Certificates, tlsCert)
|
||||
client := getWebDavClient(user, true, tlsConfig)
|
||||
err = checkBasicFunc(client)
|
||||
if assert.Error(t, err) {
|
||||
assert.Contains(t, err.Error(), "bad certificate")
|
||||
}
|
||||
|
||||
_, err = httpdtest.RemoveUser(user, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
err = os.RemoveAll(user.GetHomeDir())
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestClientCertificateAuth(t *testing.T) {
|
||||
u := getTestUser()
|
||||
u.Username = tlsClient1Username
|
||||
u.Filters.DeniedLoginMethods = []string{dataprovider.LoginMethodPassword, dataprovider.LoginMethodTLSCertificateAndPwd}
|
||||
user, _, err := httpdtest.AddUser(u, http.StatusCreated)
|
||||
assert.NoError(t, err)
|
||||
tlsConfig := &tls.Config{
|
||||
ServerName: "localhost",
|
||||
InsecureSkipVerify: true, // use this for tests only
|
||||
MinVersion: tls.VersionTLS12,
|
||||
}
|
||||
tlsCert, err := tls.X509KeyPair([]byte(client1Crt), []byte(client1Key))
|
||||
assert.NoError(t, err)
|
||||
tlsConfig.Certificates = append(tlsConfig.Certificates, tlsCert)
|
||||
// TLS username is not enabled, mutual TLS should fail
|
||||
resp, err := getTLSHTTPClient(tlsConfig).Get(fmt.Sprintf("https://%v/", webDavTLSServerAddr))
|
||||
if assert.NoError(t, err) {
|
||||
defer resp.Body.Close()
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
user.Filters.TLSUsername = dataprovider.TLSUsernameCN
|
||||
user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
|
||||
assert.NoError(t, err)
|
||||
client := getWebDavClient(user, true, tlsConfig)
|
||||
err = checkBasicFunc(client)
|
||||
assert.NoError(t, err)
|
||||
|
||||
user.Filters.DeniedLoginMethods = []string{dataprovider.LoginMethodPassword, dataprovider.LoginMethodTLSCertificate}
|
||||
user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
|
||||
assert.NoError(t, err)
|
||||
client = getWebDavClient(user, true, tlsConfig)
|
||||
err = checkBasicFunc(client)
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, err = httpdtest.RemoveUser(user, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
err = os.RemoveAll(user.GetHomeDir())
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestWrongClientCertificate(t *testing.T) {
|
||||
u := getTestUser()
|
||||
u.Username = tlsClient2Username
|
||||
u.Filters.DeniedLoginMethods = []string{dataprovider.LoginMethodTLSCertificateAndPwd}
|
||||
u.Filters.TLSUsername = dataprovider.TLSUsernameCN
|
||||
user, _, err := httpdtest.AddUser(u, http.StatusCreated)
|
||||
assert.NoError(t, err)
|
||||
tlsConfig := &tls.Config{
|
||||
ServerName: "localhost",
|
||||
InsecureSkipVerify: true, // use this for tests only
|
||||
MinVersion: tls.VersionTLS12,
|
||||
}
|
||||
tlsCert, err := tls.X509KeyPair([]byte(client1Crt), []byte(client1Key))
|
||||
assert.NoError(t, err)
|
||||
tlsConfig.Certificates = append(tlsConfig.Certificates, tlsCert)
|
||||
|
||||
// the certificate common name is client1 and it does not exists
|
||||
resp, err := getTLSHTTPClient(tlsConfig).Get(fmt.Sprintf("https://%v/", webDavTLSServerAddr))
|
||||
if assert.NoError(t, err) {
|
||||
defer resp.Body.Close()
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
user.Filters.DeniedLoginMethods = []string{dataprovider.LoginMethodPassword, dataprovider.LoginMethodTLSCertificate}
|
||||
user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
|
||||
assert.NoError(t, err)
|
||||
|
||||
// now create client1
|
||||
u = getTestUser()
|
||||
u.Username = tlsClient1Username
|
||||
u.Filters.DeniedLoginMethods = []string{dataprovider.LoginMethodPassword, dataprovider.LoginMethodTLSCertificate}
|
||||
u.Filters.TLSUsername = dataprovider.TLSUsernameCN
|
||||
user1, _, err := httpdtest.AddUser(u, http.StatusCreated)
|
||||
assert.NoError(t, err)
|
||||
|
||||
resp, err = getTLSHTTPClient(tlsConfig).Get(fmt.Sprintf("https://%v:%v@%v/", tlsClient2Username, defaultPassword,
|
||||
webDavTLSServerAddr))
|
||||
if assert.NoError(t, err) {
|
||||
defer resp.Body.Close()
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode, string(body))
|
||||
assert.Contains(t, string(body), "CN \"client1\" does not match username \"client2\"")
|
||||
}
|
||||
|
||||
_, err = httpdtest.RemoveUser(user, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
err = os.RemoveAll(user.GetHomeDir())
|
||||
assert.NoError(t, err)
|
||||
_, err = httpdtest.RemoveUser(user1, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
err = os.RemoveAll(user1.GetHomeDir())
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestClientCertificateAuthCachedUser(t *testing.T) {
|
||||
u := getTestUser()
|
||||
u.Username = tlsClient1Username
|
||||
u.Filters.TLSUsername = dataprovider.TLSUsernameCN
|
||||
u.Filters.DeniedLoginMethods = []string{dataprovider.LoginMethodTLSCertificateAndPwd}
|
||||
user, _, err := httpdtest.AddUser(u, http.StatusCreated)
|
||||
assert.NoError(t, err)
|
||||
tlsConfig := &tls.Config{
|
||||
ServerName: "localhost",
|
||||
InsecureSkipVerify: true, // use this for tests only
|
||||
MinVersion: tls.VersionTLS12,
|
||||
}
|
||||
tlsCert, err := tls.X509KeyPair([]byte(client1Crt), []byte(client1Key))
|
||||
assert.NoError(t, err)
|
||||
tlsConfig.Certificates = append(tlsConfig.Certificates, tlsCert)
|
||||
client := getWebDavClient(user, true, tlsConfig)
|
||||
err = checkBasicFunc(client)
|
||||
assert.NoError(t, err)
|
||||
// the user is now cached without a password, try a simple password login with and without TLS
|
||||
client = getWebDavClient(user, true, nil)
|
||||
err = checkBasicFunc(client)
|
||||
assert.NoError(t, err)
|
||||
|
||||
client = getWebDavClient(user, false, nil)
|
||||
err = checkBasicFunc(client)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// and now with a wrong password
|
||||
user.Password = "wrong"
|
||||
client = getWebDavClient(user, false, nil)
|
||||
err = checkBasicFunc(client)
|
||||
assert.Error(t, err)
|
||||
|
||||
// allow cert+password only
|
||||
user.Password = ""
|
||||
user.Filters.DeniedLoginMethods = []string{dataprovider.LoginMethodTLSCertificate}
|
||||
user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
|
||||
assert.NoError(t, err)
|
||||
|
||||
client = getWebDavClient(user, true, tlsConfig)
|
||||
err = checkBasicFunc(client)
|
||||
assert.NoError(t, err)
|
||||
// the user is now cached
|
||||
client = getWebDavClient(user, true, tlsConfig)
|
||||
err = checkBasicFunc(client)
|
||||
assert.NoError(t, err)
|
||||
// password auth should work too
|
||||
client = getWebDavClient(user, false, nil)
|
||||
err = checkBasicFunc(client)
|
||||
assert.NoError(t, err)
|
||||
|
||||
client = getWebDavClient(user, true, nil)
|
||||
err = checkBasicFunc(client)
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, err = httpdtest.RemoveUser(user, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
err = os.RemoveAll(user.GetHomeDir())
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestExternatAuthWithClientCert(t *testing.T) {
|
||||
if runtime.GOOS == osWindows {
|
||||
t.Skip("this test is not available on Windows")
|
||||
}
|
||||
u := getTestUser()
|
||||
u.Username = tlsClient1Username
|
||||
u.Filters.TLSUsername = dataprovider.TLSUsernameCN
|
||||
u.Filters.DeniedLoginMethods = []string{dataprovider.LoginMethodTLSCertificate, dataprovider.LoginMethodPassword}
|
||||
err := dataprovider.Close()
|
||||
assert.NoError(t, err)
|
||||
err = config.LoadConfig(configDir, "")
|
||||
assert.NoError(t, err)
|
||||
providerConf := config.GetProviderConf()
|
||||
err = os.WriteFile(extAuthPath, getExtAuthScriptContent(u, false, ""), os.ModePerm)
|
||||
assert.NoError(t, err)
|
||||
providerConf.ExternalAuthHook = extAuthPath
|
||||
providerConf.ExternalAuthScope = 0
|
||||
err = dataprovider.Initialize(providerConf, configDir, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
tlsConfig := &tls.Config{
|
||||
ServerName: "localhost",
|
||||
InsecureSkipVerify: true, // use this for tests only
|
||||
MinVersion: tls.VersionTLS12,
|
||||
}
|
||||
tlsCert, err := tls.X509KeyPair([]byte(client1Crt), []byte(client1Key))
|
||||
assert.NoError(t, err)
|
||||
tlsConfig.Certificates = append(tlsConfig.Certificates, tlsCert)
|
||||
client := getWebDavClient(u, true, tlsConfig)
|
||||
assert.NoError(t, checkBasicFunc(client))
|
||||
|
||||
resp, err := getTLSHTTPClient(tlsConfig).Get(fmt.Sprintf("https://%v:%v@%v/", tlsClient2Username, defaultPassword,
|
||||
webDavTLSServerAddr))
|
||||
if assert.NoError(t, err) {
|
||||
defer resp.Body.Close()
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode, string(body))
|
||||
assert.Contains(t, string(body), "invalid credentials")
|
||||
}
|
||||
|
||||
user, _, err := httpdtest.GetUserByUsername(tlsClient1Username, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tlsClient1Username, user.Username)
|
||||
_, err = httpdtest.RemoveUser(user, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
err = os.RemoveAll(user.GetHomeDir())
|
||||
assert.NoError(t, err)
|
||||
err = dataprovider.Close()
|
||||
assert.NoError(t, err)
|
||||
err = config.LoadConfig(configDir, "")
|
||||
assert.NoError(t, err)
|
||||
providerConf = config.GetProviderConf()
|
||||
err = dataprovider.Initialize(providerConf, configDir, true)
|
||||
assert.NoError(t, err)
|
||||
err = os.Remove(extAuthPath)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestPreLoginHookWithClientCert(t *testing.T) {
|
||||
if runtime.GOOS == osWindows {
|
||||
t.Skip("this test is not available on Windows")
|
||||
}
|
||||
u := getTestUser()
|
||||
u.Username = tlsClient1Username
|
||||
u.Filters.TLSUsername = dataprovider.TLSUsernameCN
|
||||
u.Filters.DeniedLoginMethods = []string{dataprovider.LoginMethodTLSCertificate, dataprovider.LoginMethodPassword}
|
||||
err := dataprovider.Close()
|
||||
assert.NoError(t, err)
|
||||
err = config.LoadConfig(configDir, "")
|
||||
assert.NoError(t, err)
|
||||
providerConf := config.GetProviderConf()
|
||||
err = os.WriteFile(preLoginPath, getPreLoginScriptContent(u, false), os.ModePerm)
|
||||
assert.NoError(t, err)
|
||||
providerConf.PreLoginHook = preLoginPath
|
||||
err = dataprovider.Initialize(providerConf, configDir, true)
|
||||
assert.NoError(t, err)
|
||||
_, _, err = httpdtest.GetUserByUsername(tlsClient1Username, http.StatusNotFound)
|
||||
assert.NoError(t, err)
|
||||
tlsConfig := &tls.Config{
|
||||
ServerName: "localhost",
|
||||
InsecureSkipVerify: true, // use this for tests only
|
||||
MinVersion: tls.VersionTLS12,
|
||||
}
|
||||
tlsCert, err := tls.X509KeyPair([]byte(client1Crt), []byte(client1Key))
|
||||
assert.NoError(t, err)
|
||||
tlsConfig.Certificates = append(tlsConfig.Certificates, tlsCert)
|
||||
client := getWebDavClient(u, true, tlsConfig)
|
||||
assert.NoError(t, checkBasicFunc(client))
|
||||
|
||||
user, _, err := httpdtest.GetUserByUsername(tlsClient1Username, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
// test login with an existing user
|
||||
client = getWebDavClient(user, true, tlsConfig)
|
||||
assert.NoError(t, checkBasicFunc(client))
|
||||
err = os.WriteFile(preLoginPath, getPreLoginScriptContent(user, true), os.ModePerm)
|
||||
assert.NoError(t, err)
|
||||
// update the user to remove it from the cache
|
||||
user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
|
||||
assert.NoError(t, err)
|
||||
client = getWebDavClient(user, true, tlsConfig)
|
||||
assert.Error(t, checkBasicFunc(client))
|
||||
// update the user to remove it from the cache
|
||||
user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
|
||||
assert.NoError(t, err)
|
||||
user.Status = 0
|
||||
err = os.WriteFile(preLoginPath, getPreLoginScriptContent(user, false), os.ModePerm)
|
||||
assert.NoError(t, err)
|
||||
client = getWebDavClient(user, true, tlsConfig)
|
||||
assert.Error(t, checkBasicFunc(client))
|
||||
|
||||
_, err = httpdtest.RemoveUser(user, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
err = os.RemoveAll(user.GetHomeDir())
|
||||
assert.NoError(t, err)
|
||||
err = dataprovider.Close()
|
||||
assert.NoError(t, err)
|
||||
err = config.LoadConfig(configDir, "")
|
||||
assert.NoError(t, err)
|
||||
providerConf = config.GetProviderConf()
|
||||
err = dataprovider.Initialize(providerConf, configDir, true)
|
||||
assert.NoError(t, err)
|
||||
err = os.Remove(preLoginPath)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func checkBasicFunc(client *gowebdav.Client) error {
|
||||
err := client.Connect()
|
||||
if err != nil {
|
||||
|
@ -1557,14 +2059,39 @@ func downloadFile(remoteSourcePath string, localDestPath string, expectedSize in
|
|||
return nil
|
||||
}
|
||||
|
||||
func getWebDavClient(user dataprovider.User) *gowebdav.Client {
|
||||
func getTLSHTTPClient(tlsConfig *tls.Config) *http.Client {
|
||||
customTransport := http.DefaultTransport.(*http.Transport).Clone()
|
||||
customTransport.TLSClientConfig = tlsConfig
|
||||
|
||||
return &http.Client{
|
||||
Timeout: 5 * time.Second,
|
||||
Transport: customTransport,
|
||||
}
|
||||
}
|
||||
|
||||
func getWebDavClient(user dataprovider.User, useTLS bool, tlsConfig *tls.Config) *gowebdav.Client {
|
||||
rootPath := fmt.Sprintf("http://%v/", webDavServerAddr)
|
||||
if useTLS {
|
||||
rootPath = fmt.Sprintf("https://%v/", webDavTLSServerAddr)
|
||||
if tlsConfig == nil {
|
||||
tlsConfig = &tls.Config{
|
||||
ServerName: "localhost",
|
||||
InsecureSkipVerify: true, // use this for tests only
|
||||
MinVersion: tls.VersionTLS12,
|
||||
}
|
||||
}
|
||||
}
|
||||
pwd := defaultPassword
|
||||
if user.Password != "" {
|
||||
pwd = user.Password
|
||||
}
|
||||
client := gowebdav.NewClient(rootPath, user.Username, pwd)
|
||||
client.SetTimeout(5 * time.Second)
|
||||
if tlsConfig != nil {
|
||||
customTransport := http.DefaultTransport.(*http.Transport).Clone()
|
||||
customTransport.TLSClientConfig = tlsConfig
|
||||
client.SetTransport(customTransport)
|
||||
}
|
||||
return client
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue