external auth: allow to inspect and preserve an existing user

This commit is contained in:
Nicola Murino
2021-03-26 15:19:01 +01:00
parent d5f092284a
commit 5f49af1780
8 changed files with 209 additions and 64 deletions

View File

@@ -265,7 +265,7 @@ jobs:
- name: Run tests using CockroachDB provider - name: Run tests using CockroachDB provider
run: | run: |
docker run --name crdb --health-cmd "curl -I http://127.0.0.1:8080" --health-interval 10s --health-timeout 5s --health-retries 6 -p 26257:26257 -d cockroachdb/cockroach:latest start-single-node --insecure --listen-addr 0.0.0.0:26257 docker run --rm --name crdb --health-cmd "curl -I http://127.0.0.1:8080" --health-interval 10s --health-timeout 5s --health-retries 6 -p 26257:26257 -d cockroachdb/cockroach:latest start-single-node --insecure --listen-addr 0.0.0.0:26257
docker exec crdb cockroach sql --insecure -e 'create database "sftpgo"' docker exec crdb cockroach sql --insecure -e 'create database "sftpgo"'
go test -v -p 1 -timeout 10m ./... -covermode=atomic go test -v -p 1 -timeout 10m ./... -covermode=atomic
docker stop crdb docker stop crdb

View File

@@ -397,7 +397,7 @@ func (p *BoltProvider) userExists(username string) (User, error) {
} }
u := bucket.Get([]byte(username)) u := bucket.Get([]byte(username))
if u == nil { if u == nil {
return &RecordNotFoundError{err: fmt.Sprintf("username %v does not exist", username)} return &RecordNotFoundError{err: fmt.Sprintf("username %#v does not exist", username)}
} }
folderBucket, err := getFolderBucket(tx) folderBucket, err := getFolderBucket(tx)
if err != nil { if err != nil {
@@ -465,7 +465,7 @@ func (p *BoltProvider) updateUser(user *User) error {
} }
var u []byte var u []byte
if u = bucket.Get([]byte(user.Username)); u == nil { if u = bucket.Get([]byte(user.Username)); u == nil {
return &RecordNotFoundError{err: fmt.Sprintf("username %v does not exist", user.Username)} return &RecordNotFoundError{err: fmt.Sprintf("username %#v does not exist", user.Username)}
} }
var oldUser User var oldUser User
err = json.Unmarshal(u, &oldUser) err = json.Unmarshal(u, &oldUser)

View File

@@ -2153,17 +2153,7 @@ func getPreLoginHookResponse(loginMethod, ip, protocol string, userAsJSON []byte
} }
func executePreLoginHook(username, loginMethod, ip, protocol string) (User, error) { func executePreLoginHook(username, loginMethod, ip, protocol string) (User, error) {
u, err := provider.userExists(username) u, userAsJSON, err := getUserAndJSONForHook(username)
if err != nil {
if _, ok := err.(*RecordNotFoundError); !ok {
return u, err
}
u = User{
ID: 0,
Username: username,
}
}
userAsJSON, err := json.Marshal(u)
if err != nil { if err != nil {
return u, err return u, err
} }
@@ -2173,11 +2163,11 @@ func executePreLoginHook(username, loginMethod, ip, protocol string) (User, erro
return u, fmt.Errorf("pre-login hook error: %v, elapsed %v", err, time.Since(startTime)) return u, fmt.Errorf("pre-login hook error: %v, elapsed %v", err, time.Since(startTime))
} }
providerLog(logger.LevelDebug, "pre-login hook completed, elapsed: %v", time.Since(startTime)) providerLog(logger.LevelDebug, "pre-login hook completed, elapsed: %v", time.Since(startTime))
if strings.TrimSpace(string(out)) == "" { if utils.IsByteArrayEmpty(out) {
providerLog(logger.LevelDebug, "empty response from pre-login hook, no modification requested for user %#v id: %v", providerLog(logger.LevelDebug, "empty response from pre-login hook, no modification requested for user %#v id: %v",
username, u.ID) username, u.ID)
if u.ID == 0 { if u.ID == 0 {
return u, &RecordNotFoundError{err: fmt.Sprintf("username %v does not exist", username)} return u, &RecordNotFoundError{err: fmt.Sprintf("username %#v does not exist", username)}
} }
return u, nil return u, nil
} }
@@ -2276,7 +2266,15 @@ func ExecutePostLoginHook(user *User, loginMethod, ip, protocol string, err erro
}() }()
} }
func getExternalAuthResponse(username, password, pkey, keyboardInteractive, ip, protocol, tlsCert string) ([]byte, error) { func getExternalAuthResponse(username, password, pkey, keyboardInteractive, ip, protocol string, cert *x509.Certificate, userAsJSON []byte) ([]byte, error) {
var tlsCert string
if cert != nil {
var err error
tlsCert, err = utils.EncodeTLSCertToPem(cert)
if err != nil {
return nil, err
}
}
if strings.HasPrefix(config.ExternalAuthHook, "http") { if strings.HasPrefix(config.ExternalAuthHook, "http") {
var url *url.URL var url *url.URL
var result []byte var result []byte
@@ -2294,6 +2292,9 @@ func getExternalAuthResponse(username, password, pkey, keyboardInteractive, ip,
authRequest["protocol"] = protocol authRequest["protocol"] = protocol
authRequest["keyboard_interactive"] = keyboardInteractive authRequest["keyboard_interactive"] = keyboardInteractive
authRequest["tls_cert"] = tlsCert authRequest["tls_cert"] = tlsCert
if len(userAsJSON) > 0 {
authRequest["user"] = string(userAsJSON)
}
authRequestAsJSON, err := json.Marshal(authRequest) authRequestAsJSON, err := json.Marshal(authRequest)
if err != nil { if err != nil {
providerLog(logger.LevelWarn, "error serializing external auth request: %v", err) providerLog(logger.LevelWarn, "error serializing external auth request: %v", err)
@@ -2317,6 +2318,7 @@ func getExternalAuthResponse(username, password, pkey, keyboardInteractive, ip,
cmd := exec.CommandContext(ctx, config.ExternalAuthHook) cmd := exec.CommandContext(ctx, config.ExternalAuthHook)
cmd.Env = append(os.Environ(), cmd.Env = append(os.Environ(),
fmt.Sprintf("SFTPGO_AUTHD_USERNAME=%v", username), fmt.Sprintf("SFTPGO_AUTHD_USERNAME=%v", username),
fmt.Sprintf("SFTPGO_AUTHD_USER=%v", string(userAsJSON)),
fmt.Sprintf("SFTPGO_AUTHD_IP=%v", ip), fmt.Sprintf("SFTPGO_AUTHD_IP=%v", ip),
fmt.Sprintf("SFTPGO_AUTHD_PASSWORD=%v", password), fmt.Sprintf("SFTPGO_AUTHD_PASSWORD=%v", password),
fmt.Sprintf("SFTPGO_AUTHD_PUBLIC_KEY=%v", pkey), fmt.Sprintf("SFTPGO_AUTHD_PUBLIC_KEY=%v", pkey),
@@ -2328,27 +2330,30 @@ func getExternalAuthResponse(username, password, pkey, keyboardInteractive, ip,
func doExternalAuth(username, password string, pubKey []byte, keyboardInteractive, ip, protocol string, tlsCert *x509.Certificate) (User, error) { func doExternalAuth(username, password string, pubKey []byte, keyboardInteractive, ip, protocol string, tlsCert *x509.Certificate) (User, error) {
var user User var user User
var pkey, cert string
if len(pubKey) > 0 { pkey, err := utils.GetSSHPublicKeyAsString(pubKey)
k, err := ssh.ParsePublicKey(pubKey) if err != nil {
if err != nil { return user, err
return user, err
}
pkey = string(ssh.MarshalAuthorizedKey(k))
} }
if tlsCert != nil { u, userAsJSON, err := getUserAndJSONForHook(username)
var err error if err != nil {
cert, err = utils.EncodeTLSCertToPem(tlsCert) return user, err
if err != nil {
return user, err
}
} }
startTime := time.Now() startTime := time.Now()
out, err := getExternalAuthResponse(username, password, pkey, keyboardInteractive, ip, protocol, cert) out, err := getExternalAuthResponse(username, password, pkey, keyboardInteractive, ip, protocol, tlsCert, userAsJSON)
if err != nil { if err != nil {
return user, fmt.Errorf("external auth error: %v, elapsed: %v", err, time.Since(startTime)) return user, fmt.Errorf("external auth error: %v, elapsed: %v", err, time.Since(startTime))
} }
providerLog(logger.LevelDebug, "external auth completed, elapsed: %v", time.Since(startTime)) providerLog(logger.LevelDebug, "external auth completed, elapsed: %v", time.Since(startTime))
if utils.IsByteArrayEmpty(out) {
providerLog(logger.LevelDebug, "empty response from external hook, no modification requested for user %#v id: %v",
username, u.ID)
if u.ID == 0 {
return u, &RecordNotFoundError{err: fmt.Sprintf("username %#v does not exist", username)}
}
return u, nil
}
err = json.Unmarshal(out, &user) err = json.Unmarshal(out, &user)
if err != nil { if err != nil {
return user, fmt.Errorf("invalid external auth response: %v", err) return user, fmt.Errorf("invalid external auth response: %v", err)
@@ -2366,8 +2371,10 @@ func doExternalAuth(username, password string, pubKey []byte, keyboardInteractiv
// for example an SFTP user logins using "user1" or "user2" and the external auth // for example an SFTP user logins using "user1" or "user2" and the external auth
// returns "user" in both cases, so we use the username returned from // returns "user" in both cases, so we use the username returned from
// external auth and not the one used to login // external auth and not the one used to login
u, err := provider.userExists(user.Username) if user.Username != username {
if err == nil { u, err = provider.userExists(user.Username)
}
if u.ID > 0 && err == nil {
user.ID = u.ID user.ID = u.ID
user.UsedQuotaSize = u.UsedQuotaSize user.UsedQuotaSize = u.UsedQuotaSize
user.UsedQuotaFiles = u.UsedQuotaFiles user.UsedQuotaFiles = u.UsedQuotaFiles
@@ -2383,6 +2390,26 @@ func doExternalAuth(username, password string, pubKey []byte, keyboardInteractiv
return provider.userExists(user.Username) return provider.userExists(user.Username)
} }
func getUserAndJSONForHook(username string) (User, []byte, error) {
var userAsJSON []byte
u, err := provider.userExists(username)
if err != nil {
if _, ok := err.(*RecordNotFoundError); !ok {
return u, userAsJSON, err
}
u = User{
ID: 0,
Username: username,
}
return u, userAsJSON, nil
}
userAsJSON, err = json.Marshal(u)
if err != nil {
return u, userAsJSON, err
}
return u, userAsJSON, err
}
func providerLog(level logger.LogLevel, format string, v ...interface{}) { func providerLog(level logger.LogLevel, format string, v ...interface{}) {
logger.Log(level, logSender, "", format, v...) logger.Log(level, logSender, "", format, v...)
} }

View File

@@ -5,6 +5,7 @@ To enable external authentication, you must set the absolute path of your authen
The external program can read the following environment variables to get info about the user trying to authenticate: The external program can read the following environment variables to get info about the user trying to authenticate:
- `SFTPGO_AUTHD_USERNAME` - `SFTPGO_AUTHD_USERNAME`
- `SFTPGO_AUTHD_USER`, STPGo user serialized as JSON, empty if the user does not exist within the data provider
- `SFTPGO_AUTHD_IP` - `SFTPGO_AUTHD_IP`
- `SFTPGO_AUTHD_PROTOCOL`, possible values are `SSH`, `FTP`, `DAV` - `SFTPGO_AUTHD_PROTOCOL`, possible values are `SSH`, `FTP`, `DAV`
- `SFTPGO_AUTHD_PASSWORD`, not empty for password authentication - `SFTPGO_AUTHD_PASSWORD`, not empty for password authentication
@@ -13,21 +14,32 @@ The external program can read the following environment variables to get info ab
- `SFTPGO_AUTHD_TLS_CERT`, TLS client certificate PEM encoded. Not empty for TLS certificate authentication - `SFTPGO_AUTHD_TLS_CERT`, TLS client certificate PEM encoded. Not empty for TLS certificate authentication
Previous global environment variables aren't cleared when the script is called. The content of these variables is _not_ quoted. They may contain special characters. They are under the control of a possibly malicious remote user. Previous global environment variables aren't cleared when the script is called. The content of these variables is _not_ quoted. They may contain special characters. They are under the control of a possibly malicious remote user.
The program must write, on its standard output, a valid SFTPGo user serialized as JSON if the authentication succeeds or a user with an empty username if the authentication fails. The program can inspect the SFTPGo user, if it exists, using the `SFTPGO_AUTHD_USER` environment variable.
The program must write, on its standard output:
- a valid SFTPGo user serialized as JSON if the authentication succeeds. The user will be added/updated within the defined data provider
- an empty string, or no response at all, if authentication succeeds and the existing SFTPGo user does not need to be updated
- a user with an empty username if the authentication fails
If the hook is an HTTP URL then it will be invoked as HTTP POST. The request body will contain a JSON serialized struct with the following fields: If the hook is an HTTP URL then it will be invoked as HTTP POST. The request body will contain a JSON serialized struct with the following fields:
- `username` - `username`
- `ip` - `ip`
- `user`, STPGo user serialized as JSON, omitted if the user does not exist within the data provider
- `protocol`, possible values are `SSH`, `FTP`, `DAV` - `protocol`, possible values are `SSH`, `FTP`, `DAV`
- `password`, not empty for password authentication - `password`, not empty for password authentication
- `public_key`, not empty for public key authentication - `public_key`, not empty for public key authentication
- `keyboard_interactive`, not empty for keyboard interactive authentication - `keyboard_interactive`, not empty for keyboard interactive authentication
- `tls_cert`, TLS client certificate PEM encoded. Not empty for TLS certificate authentication - `tls_cert`, TLS client certificate PEM encoded. Not empty for TLS certificate authentication
If authentication succeeds the HTTP response code must be 200 and the response body a valid SFTPGo user serialized as JSON. If the authentication fails the HTTP response code must be != 200 or the response body must be empty. If authentication succeeds the HTTP response code must be 200 and the response body can be:
If the authentication succeeds, the user will be automatically added/updated inside the defined data provider. Actions defined for users added/updated will not be executed in this case and an already logged in user with the same username will not be disconnected, you have to handle these things yourself. - a valid SFTPGo user serialized as JSON. The user will be added/updated within the defined data provider
- empty, the existing SFTPGo user does not need to be updated
If the authentication fails the HTTP response code must be != 200.
Actions defined for users added/updated will not be executed in this case and an already logged in user with the same username will not be disconnected.
The program hook must finish within 30 seconds, the HTTP hook timeout will use the global configuration for HTTP clients. The program hook must finish within 30 seconds, the HTTP hook timeout will use the global configuration for HTTP clients.

14
go.mod
View File

@@ -8,14 +8,14 @@ require (
github.com/Azure/azure-storage-blob-go v0.13.0 github.com/Azure/azure-storage-blob-go v0.13.0
github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962 github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962
github.com/StackExchange/wmi v0.0.0-20210224194228-fe8f1750fd46 // indirect github.com/StackExchange/wmi v0.0.0-20210224194228-fe8f1750fd46 // indirect
github.com/alexedwards/argon2id v0.0.0-20201228115903-cf543ebc1f7b github.com/alexedwards/argon2id v0.0.0-20210326052512-e2135f7c9c77
github.com/aws/aws-sdk-go v1.38.3 github.com/aws/aws-sdk-go v1.38.6
github.com/cockroachdb/cockroach-go/v2 v2.1.0 github.com/cockroachdb/cockroach-go/v2 v2.1.0
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf // indirect github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf // indirect
github.com/eikenb/pipeat v0.0.0-20200430215831-470df5986b6d github.com/eikenb/pipeat v0.0.0-20200430215831-470df5986b6d
github.com/fclairamb/ftpserverlib v0.13.0 github.com/fclairamb/ftpserverlib v0.13.0
github.com/frankban/quicktest v1.11.3 // indirect github.com/frankban/quicktest v1.11.3 // indirect
github.com/go-chi/chi/v5 v5.0.1 github.com/go-chi/chi/v5 v5.0.2
github.com/go-chi/jwtauth/v5 v5.0.0 github.com/go-chi/jwtauth/v5 v5.0.0
github.com/go-chi/render v1.0.1 github.com/go-chi/render v1.0.1
github.com/go-ole/go-ole v1.2.5 // indirect github.com/go-ole/go-ole v1.2.5 // indirect
@@ -30,11 +30,11 @@ require (
github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-retryablehttp v0.6.8 github.com/hashicorp/go-retryablehttp v0.6.8
github.com/jlaffaye/ftp v0.0.0-20201112195030-9aae4d151126 github.com/jlaffaye/ftp v0.0.0-20201112195030-9aae4d151126
github.com/klauspost/cpuid/v2 v2.0.5 // indirect github.com/klauspost/cpuid/v2 v2.0.6 // indirect
github.com/lestrrat-go/backoff/v2 v2.0.8 // indirect github.com/lestrrat-go/backoff/v2 v2.0.8 // indirect
github.com/lestrrat-go/jwx v1.1.5 github.com/lestrrat-go/jwx v1.1.5
github.com/lib/pq v1.10.0 github.com/lib/pq v1.10.0
github.com/magiconair/properties v1.8.4 // indirect github.com/magiconair/properties v1.8.5 // indirect
github.com/mattn/go-sqlite3 v1.14.6 github.com/mattn/go-sqlite3 v1.14.6
github.com/miekg/dns v1.1.41 // indirect github.com/miekg/dns v1.1.41 // indirect
github.com/minio/sha256-simd v1.0.0 github.com/minio/sha256-simd v1.0.0
@@ -48,7 +48,7 @@ require (
github.com/prometheus/common v0.20.0 // indirect github.com/prometheus/common v0.20.0 // indirect
github.com/rs/cors v1.7.1-0.20200626170627-8b4a00bd362b github.com/rs/cors v1.7.1-0.20200626170627-8b4a00bd362b
github.com/rs/xid v1.2.1 github.com/rs/xid v1.2.1
github.com/rs/zerolog v1.20.0 github.com/rs/zerolog v1.21.0
github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/shirou/gopsutil/v3 v3.21.2 github.com/shirou/gopsutil/v3 v3.21.2
github.com/spf13/afero v1.6.0 github.com/spf13/afero v1.6.0
@@ -69,7 +69,7 @@ require (
golang.org/x/oauth2 v0.0.0-20210323180902-22b0adad7558 // indirect golang.org/x/oauth2 v0.0.0-20210323180902-22b0adad7558 // indirect
golang.org/x/sys v0.0.0-20210324051608-47abb6519492 golang.org/x/sys v0.0.0-20210324051608-47abb6519492
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
google.golang.org/api v0.42.0 google.golang.org/api v0.43.0
gopkg.in/ini.v1 v1.62.0 // indirect gopkg.in/ini.v1 v1.62.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0 gopkg.in/natefinch/lumberjack.v2 v2.0.0
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect

29
go.sum
View File

@@ -105,8 +105,8 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/alexedwards/argon2id v0.0.0-20201228115903-cf543ebc1f7b h1:jEg+fE+POnmUy40B+aSKEPqZDmsdl55hZU0YKXEzz1k= github.com/alexedwards/argon2id v0.0.0-20210326052512-e2135f7c9c77 h1:X6U+/fhTYeDYS3sN4xHcoORJhhar+zSgrNeraapuRK4=
github.com/alexedwards/argon2id v0.0.0-20201228115903-cf543ebc1f7b/go.mod h1:Kmn5t2Rb93Q4NTprN4+CCgARGvigKMJyxP0WckpTUp0= github.com/alexedwards/argon2id v0.0.0-20210326052512-e2135f7c9c77/go.mod h1:Kmn5t2Rb93Q4NTprN4+CCgARGvigKMJyxP0WckpTUp0=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
@@ -118,8 +118,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.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.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.36.1/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
github.com/aws/aws-sdk-go v1.38.3 h1:QCL/le04oAz2jELMRSuJVjGT7H+4hhoQc66eMPCfU/k= github.com/aws/aws-sdk-go v1.38.6 h1:h0AKIaz/A1kEJ50HxCv7tL1GW+KbxYbp75+lZ/nvFOI=
github.com/aws/aws-sdk-go v1.38.3/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/aws/aws-sdk-go v1.38.6/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= 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 v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
@@ -217,8 +217,8 @@ github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm
github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do= 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/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs=
github.com/go-chi/chi/v5 v5.0.1 h1:ALxjCrTf1aflOlkhMnCUP86MubbWFrzB3gkRPReLpTo= github.com/go-chi/chi/v5 v5.0.2 h1:4xKeALZdMEsuI5s05PU2Bm89Uc5iM04qFubUCl5LfAQ=
github.com/go-chi/chi/v5 v5.0.1/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/chi/v5 v5.0.2/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/jwtauth/v5 v5.0.0 h1:FjyoBQ0sH6/OSBCTXdRMMd7Eis3UjmsX2wKTSucFn+g= github.com/go-chi/jwtauth/v5 v5.0.0 h1:FjyoBQ0sH6/OSBCTXdRMMd7Eis3UjmsX2wKTSucFn+g=
github.com/go-chi/jwtauth/v5 v5.0.0/go.mod h1:wdYCsXCBuihmcGwLdfVgZ4LhDLOZHfyF+Fd5mEjiGPM= github.com/go-chi/jwtauth/v5 v5.0.0/go.mod h1:wdYCsXCBuihmcGwLdfVgZ4LhDLOZHfyF+Fd5mEjiGPM=
github.com/go-chi/render v1.0.1 h1:4/5tis2cKaNdnv9zFLfXzcquC9HbeZgCnxGnKrltBS8= github.com/go-chi/render v1.0.1 h1:4/5tis2cKaNdnv9zFLfXzcquC9HbeZgCnxGnKrltBS8=
@@ -497,8 +497,8 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.0.5 h1:qnfhwbFriwDIX51QncuNU5mEMf+6KE3t7O8V2KQl3Dg= github.com/klauspost/cpuid/v2 v2.0.6 h1:dQ5ueTiftKxp0gyjKSx5+8BtPWkyQbd95m8Gys/RarI=
github.com/klauspost/cpuid/v2 v2.0.5/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.6/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
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.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.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= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@@ -542,8 +542,8 @@ github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-b
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.4 h1:8KGKTcQQGm0Kv7vEbKFErAoAOFyyacLStRtQSeYtvkY= github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
@@ -692,8 +692,8 @@ github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
github.com/rs/zerolog v1.20.0 h1:38k9hgtUBdxFwE34yS8rTHmHBa4eN16E4DJlv177LNs= github.com/rs/zerolog v1.21.0 h1:Q3vdXlfLNT+OftyBHsU0Y445MD+8m8axjKgf2si0QcM=
github.com/rs/zerolog v1.20.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo= github.com/rs/zerolog v1.21.0/go.mod h1:ZPhntP/xmq1nnND05hhpAh2QMhSsA4UN3MGZ6O2J3hM=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@@ -974,7 +974,6 @@ golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgw
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@@ -1054,8 +1053,9 @@ google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
google.golang.org/api v0.42.0 h1:uqATLkpxiBrhrvFoebXUjvyzE9nQf+pVyy0Z0IHE+fc=
google.golang.org/api v0.42.0/go.mod h1:+Oj4s6ch2SEGtPjGqfUfZonBH0GjQH89gTeKKAEGZKI= google.golang.org/api v0.42.0/go.mod h1:+Oj4s6ch2SEGtPjGqfUfZonBH0GjQH89gTeKKAEGZKI=
google.golang.org/api v0.43.0 h1:4sAyIHT6ZohtAQDoxws+ez7bROYmUlOVvsUscYCDTqA=
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -1113,6 +1113,7 @@ google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210312152112-fc591d9ea70f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210312152112-fc591d9ea70f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210323160006-e668133fea6a h1:XVaQ1+BDKvrRcgppHhtAaniHCKyV5xJAvymwsPHHFaE= google.golang.org/genproto v0.0.0-20210323160006-e668133fea6a h1:XVaQ1+BDKvrRcgppHhtAaniHCKyV5xJAvymwsPHHFaE=
google.golang.org/genproto v0.0.0-20210323160006-e668133fea6a/go.mod h1:f2Bd7+2PlaVKmvKQ52aspJZXIDaRQBVdOOBfJ5i8OEs= google.golang.org/genproto v0.0.0-20210323160006-e668133fea6a/go.mod h1:f2Bd7+2PlaVKmvKQ52aspJZXIDaRQBVdOOBfJ5i8OEs=
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=

View File

@@ -2175,7 +2175,7 @@ func TestLoginExternalAuthPwdAndPubKey(t *testing.T) {
err = config.LoadConfig(configDir, "") err = config.LoadConfig(configDir, "")
assert.NoError(t, err) assert.NoError(t, err)
providerConf := config.GetProviderConf() providerConf := config.GetProviderConf()
err = os.WriteFile(extAuthPath, getExtAuthScriptContent(u, false, ""), os.ModePerm) err = os.WriteFile(extAuthPath, getExtAuthScriptContent(u, false, false, ""), os.ModePerm)
assert.NoError(t, err) assert.NoError(t, err)
providerConf.ExternalAuthHook = extAuthPath providerConf.ExternalAuthHook = extAuthPath
providerConf.ExternalAuthScope = 0 providerConf.ExternalAuthScope = 0
@@ -2202,7 +2202,7 @@ func TestLoginExternalAuthPwdAndPubKey(t *testing.T) {
usePubKey = false usePubKey = false
u = getTestUser(usePubKey) u = getTestUser(usePubKey)
u.PublicKeys = []string{} u.PublicKeys = []string{}
err = os.WriteFile(extAuthPath, getExtAuthScriptContent(u, false, ""), os.ModePerm) err = os.WriteFile(extAuthPath, getExtAuthScriptContent(u, false, false, ""), os.ModePerm)
assert.NoError(t, err) assert.NoError(t, err)
client, err = getSftpClient(u, usePubKey) client, err = getSftpClient(u, usePubKey)
if assert.NoError(t, err) { if assert.NoError(t, err) {
@@ -2231,6 +2231,81 @@ func TestLoginExternalAuthPwdAndPubKey(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
} }
func TestExternalAuthEmptyResponse(t *testing.T) {
if runtime.GOOS == osWindows {
t.Skip("this test is not available on Windows")
}
usePubKey := false
u := getTestUser(usePubKey)
u.QuotaFiles = 1000
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, false, ""), os.ModePerm)
assert.NoError(t, err)
providerConf.ExternalAuthHook = extAuthPath
providerConf.ExternalAuthScope = 0
err = dataprovider.Initialize(providerConf, configDir, true)
assert.NoError(t, err)
testFileSize := int64(65535)
// the user will be created
client, err := getSftpClient(u, usePubKey)
if assert.NoError(t, err) {
defer client.Close()
testFilePath := filepath.Join(homeBasePath, testFileName)
err = createTestFile(testFilePath, testFileSize)
assert.NoError(t, err)
err = sftpUploadFile(testFilePath, testFileName, testFileSize, client)
assert.NoError(t, err)
err = os.Remove(testFilePath)
assert.NoError(t, err)
}
user, _, err := httpdtest.GetUserByUsername(defaultUsername, http.StatusOK)
assert.NoError(t, err)
assert.Equal(t, 0, len(user.PublicKeys))
assert.Equal(t, testFileSize, user.UsedQuotaSize)
assert.Equal(t, 1, user.UsedQuotaFiles)
// now modify the user
user.MaxSessions = 10
user.QuotaFiles = 100
user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
assert.NoError(t, err)
err = os.WriteFile(extAuthPath, getExtAuthScriptContent(u, false, true, ""), os.ModePerm)
assert.NoError(t, err)
client, err = getSftpClient(u, usePubKey)
if assert.NoError(t, err) {
defer client.Close()
err = checkBasicSFTP(client)
assert.NoError(t, err)
}
user, _, err = httpdtest.GetUserByUsername(defaultUsername, http.StatusOK)
assert.NoError(t, err)
assert.Equal(t, 10, user.MaxSessions)
assert.Equal(t, 100, user.QuotaFiles)
_, 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 TestExternalAuthDifferentUsername(t *testing.T) { func TestExternalAuthDifferentUsername(t *testing.T) {
if runtime.GOOS == osWindows { if runtime.GOOS == osWindows {
t.Skip("this test is not available on Windows") t.Skip("this test is not available on Windows")
@@ -2244,7 +2319,7 @@ func TestExternalAuthDifferentUsername(t *testing.T) {
err = config.LoadConfig(configDir, "") err = config.LoadConfig(configDir, "")
assert.NoError(t, err) assert.NoError(t, err)
providerConf := config.GetProviderConf() providerConf := config.GetProviderConf()
err = os.WriteFile(extAuthPath, getExtAuthScriptContent(u, false, extAuthUsername), os.ModePerm) err = os.WriteFile(extAuthPath, getExtAuthScriptContent(u, false, false, extAuthUsername), os.ModePerm)
assert.NoError(t, err) assert.NoError(t, err)
providerConf.ExternalAuthHook = extAuthPath providerConf.ExternalAuthHook = extAuthPath
providerConf.ExternalAuthScope = 0 providerConf.ExternalAuthScope = 0
@@ -2327,7 +2402,7 @@ func TestLoginExternalAuth(t *testing.T) {
err = config.LoadConfig(configDir, "") err = config.LoadConfig(configDir, "")
assert.NoError(t, err) assert.NoError(t, err)
providerConf := config.GetProviderConf() providerConf := config.GetProviderConf()
err = os.WriteFile(extAuthPath, getExtAuthScriptContent(u, false, ""), os.ModePerm) err = os.WriteFile(extAuthPath, getExtAuthScriptContent(u, false, false, ""), os.ModePerm)
assert.NoError(t, err) assert.NoError(t, err)
providerConf.ExternalAuthHook = extAuthPath providerConf.ExternalAuthHook = extAuthPath
providerConf.ExternalAuthScope = authScope providerConf.ExternalAuthScope = authScope
@@ -2389,7 +2464,7 @@ func TestLoginExternalAuthInteractive(t *testing.T) {
err = config.LoadConfig(configDir, "") err = config.LoadConfig(configDir, "")
assert.NoError(t, err) assert.NoError(t, err)
providerConf := config.GetProviderConf() providerConf := config.GetProviderConf()
err = os.WriteFile(extAuthPath, getExtAuthScriptContent(u, false, ""), os.ModePerm) err = os.WriteFile(extAuthPath, getExtAuthScriptContent(u, false, false, ""), os.ModePerm)
assert.NoError(t, err) assert.NoError(t, err)
providerConf.ExternalAuthHook = extAuthPath providerConf.ExternalAuthHook = extAuthPath
providerConf.ExternalAuthScope = 4 providerConf.ExternalAuthScope = 4
@@ -2443,7 +2518,7 @@ func TestLoginExternalAuthErrors(t *testing.T) {
err = config.LoadConfig(configDir, "") err = config.LoadConfig(configDir, "")
assert.NoError(t, err) assert.NoError(t, err)
providerConf := config.GetProviderConf() providerConf := config.GetProviderConf()
err = os.WriteFile(extAuthPath, getExtAuthScriptContent(u, true, ""), os.ModePerm) err = os.WriteFile(extAuthPath, getExtAuthScriptContent(u, true, false, ""), os.ModePerm)
assert.NoError(t, err) assert.NoError(t, err)
providerConf.ExternalAuthHook = extAuthPath providerConf.ExternalAuthHook = extAuthPath
providerConf.ExternalAuthScope = 0 providerConf.ExternalAuthScope = 0
@@ -8978,8 +9053,11 @@ func getKeyboardInteractiveScriptContent(questions []string, sleepTime int, nonJ
return content return content
} }
func getExtAuthScriptContent(user dataprovider.User, nonJSONResponse bool, username string) []byte { func getExtAuthScriptContent(user dataprovider.User, nonJSONResponse, emptyResponse bool, username string) []byte {
extAuthContent := []byte("#!/bin/sh\n\n") extAuthContent := []byte("#!/bin/sh\n\n")
if emptyResponse {
return extAuthContent
}
extAuthContent = append(extAuthContent, []byte(fmt.Sprintf("if test \"$SFTPGO_AUTHD_USERNAME\" = \"%v\"; then\n", user.Username))...) extAuthContent = append(extAuthContent, []byte(fmt.Sprintf("if test \"$SFTPGO_AUTHD_USERNAME\" = \"%v\"; then\n", user.Username))...)
if len(username) > 0 { if len(username) > 0 {
user.Username = username user.Username = username

View File

@@ -2,6 +2,7 @@
package utils package utils
import ( import (
"bytes"
"crypto/aes" "crypto/aes"
"crypto/cipher" "crypto/cipher"
"crypto/ecdsa" "crypto/ecdsa"
@@ -489,3 +490,29 @@ func CheckTCP4Port(port int) {
} }
listener.Close() listener.Close()
} }
// IsByteArrayEmpty return true if the byte array is empty or a new line
func IsByteArrayEmpty(b []byte) bool {
if len(b) == 0 {
return true
}
if bytes.Equal(b, []byte("\n")) {
return true
}
if bytes.Equal(b, []byte("\r\n")) {
return true
}
return false
}
// GetSSHPublicKeyAsString returns an SSH public key serialized as string
func GetSSHPublicKeyAsString(pubKey []byte) (string, error) {
if len(pubKey) == 0 {
return "", nil
}
k, err := ssh.ParsePublicKey(pubKey)
if err != nil {
return "", err
}
return string(ssh.MarshalAuthorizedKey(k)), nil
}